Tôi có một tình huống tôi đang gặp bế tắc và tôi nghĩ rằng tôi đã thu hẹp được thủ phạm, nhưng tôi không chắc mình có thể làm gì để khắc phục nó.
Đây là trên một môi trường sản xuất chạy SQL Server 2008 R2.
Để cung cấp cho bạn một cái nhìn hơi đơn giản về tình huống:
Tôi có 3 bảng như được định nghĩa dưới đây:
TABLE activity (
id, -- PK
...
)
TABLE member_activity (
member_id, -- PK col 1
activity_id, -- PK col 2
...
)
TABLE follow (
id, -- PK
follower_id,
member_id,
...
)
Các member_activity
bảng có một hợp chất Primary Key định nghĩa là member_id, activity_id
, bởi vì tôi chỉ có bao giờ cần phải tìm kiếm dữ liệu trên bảng mà theo cách đó.
Tôi cũng có một chỉ mục không bao gồm trên follow
:
CREATE NONCLUSTERED INDEX [IX_follow_member_id_includes]
ON follow ( member_id ASC ) INCLUDE ( follower_id )
Ngoài ra, tôi có chế độ xem liên kết Schema network_activity
được xác định như sau:
CREATE VIEW network_activity
WITH SCHEMABINDING
AS
SELECT
follow.follower_id as member_id,
member_activity.activity_id as activity_id,
COUNT_BIG(*) AS cb
FROM member_activity
INNER JOIN follow ON follow.member_id = member_activity.member_id
INNER JOIN activity ON activity.id = member_activity.activity_id
GROUP BY follow.follower_id, member_activity.activity_id
Mà cũng có một chỉ mục cụm duy nhất:
CREATE UNIQUE CLUSTERED INDEX [IX_network_activity_unique_member_id_activity_id]
ON network_activity
(
member_id ASC,
activity_id ASC
)
Bây giờ, tôi có hai thủ tục lưu trữ bế tắc. Họ trải qua quá trình sau đây:
-- SP1: insert activity
-----------------------
INSERT INTO activity (...)
SELECT ... FROM member_activity WHERE member_id = @a AND activity_id = @b
INSERT INTO member_activity (...)
-- SP2: insert follow
---------------------
SELECT follow WHERE member_id = @x AND follower_id = @y
INSERT INTO follow (...)
Cả hai thủ tục này đều chạy trong cách ly READ CAMEDED. Tôi đã quản lý để truy vấn đầu ra 1222 sự kiện mở rộng và đã diễn giải những điều sau đây liên quan đến các bế tắc:
SP1 đang chờ
RangeS-S
khóa khóa trênIX_follow_member_id_includes
chỉ mục trong khi SP2 giữ khóa (X) xung độtSP2 đang chờ
S
khóa chế độPK_member_activity
trong khi SP1 giữ khóa (X) xung đột
Sự bế tắc dường như đang xảy ra ở dòng cuối cùng của mỗi truy vấn (phần chèn). Điều không rõ ràng với tôi là tại sao SP1 lại muốn khóa trên IX_follow-member_id_includes
chỉ mục. Liên kết duy nhất, với tôi, dường như là từ quan điểm được lập chỉ mục này, đó là lý do tại sao tôi đưa nó vào.
Điều gì sẽ là cách tốt nhất để tôi ngăn chặn những bế tắc này xảy ra? Bất kì sự trợ giúp nào đều được đánh giá cao. Tôi không có nhiều kinh nghiệm trong việc giải quyết các vấn đề bế tắc.
Xin vui lòng cho tôi biết nếu có thêm thông tin tôi có thể cung cấp có thể giúp đỡ!
Cảm ơn trước.
Chỉnh sửa 1: Thêm một số thông tin cho mỗi yêu cầu.
Đây là đầu ra 1222 từ bế tắc này:
<deadlock>
<victim-list>
<victimProcess id="process4c6672748" />
</victim-list>
<process-list>
<process id="process4c6672748" taskpriority="0" logused="332" waitresource="KEY: 8:72057594104905728 (25014f77eaba)" waittime="581" ownerId="474698706" transactionname="INSERT" lasttranstarted="2014-07-03T17:03:12.287" XDES="0x298487970" lockMode="RangeS-S" schedulerid="1" kpid="972" status="suspended" spid="79" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-07-03T17:03:12.283" lastbatchcompleted="2014-07-03T17:03:12.283" lastattention="2014-07-03T10:25:00.283" clientapp=".Net SqlClient Data Provider" hostname="WIN08CLYDESDALE" hostpid="4596" loginname="TechPro" isolationlevel="read committed (2)" xactid="474698706" currentdb="8" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="" line="7" stmtstart="1194" stmtend="1434" sqlhandle="0x02000000a26bb72a2b220406876cad09c22242e5265c82e6" />
<frame procname="" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000" />
</executionStack>
<inputbuf> <!-- SP 1 --> </inputbuf>
</process>
<process id="process6cddc5b88" taskpriority="0" logused="456" waitresource="KEY: 8:72057594098679808 (89013169fc76)" waittime="567" ownerId="474698698" transactionname="INSERT" lasttranstarted="2014-07-03T17:03:12.283" XDES="0x30c459970" lockMode="S" schedulerid="4" kpid="4204" status="suspended" spid="70" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2014-07-03T17:03:12.283" lastbatchcompleted="2014-07-03T17:03:12.283" lastattention="2014-07-03T15:04:55.870" clientapp=".Net SqlClient Data Provider" hostname="WIN08CLYDESDALE" hostpid="4596" loginname="TechPro" isolationlevel="read committed (2)" xactid="474698698" currentdb="8" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="" line="18" stmtstart="942" stmtend="1250" sqlhandle="0x03000800ca458d315ee9130100a300000100000000000000" />
</executionStack>
<inputbuf> <!-- SP 2 --> </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594104905728" dbid="8" objectname="" indexname="" id="lock33299fc00" mode="X" associatedObjectId="72057594104905728">
<owner-list>
<owner id="process6cddc5b88" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process4c6672748" mode="RangeS-S" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594098679808" dbid="8" objectname="" indexname="" id="lockb7e2ba80" mode="X" associatedObjectId="72057594098679808">
<owner-list>
<owner id="process4c6672748" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process6cddc5b88" mode="S" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Trong trường hợp này,
AssociObjectId 72057594098679808 tương ứng với member_activity, PK_member_activity
AssociObjectId 72057594104905728 tương ứng với follow, IX_follow_member_id_includes
Ngoài ra, đây là một hình ảnh chính xác hơn về những gì SP1 và SP2 đang làm
-- SP1: insert activity
-----------------------
DECLARE @activityId INT
INSERT INTO activity (field1, field2)
VALUES (@field1, @field2)
SET @activityId = SCOPE_IDENTITY();
IF NOT EXISTS(
SELECT TOP 1 member_id
FROM member_activity
WHERE member_id = @m1 AND activity_id = @activityId
)
INSERT INTO member_activity (member_id, activity_id, field1)
VALUES (@m1, @activityId, @field1)
IF NOT EXISTS(
SELECT TOP 1 member_id
FROM member_activity
WHERE member_id = @m2 AND activity_id = @activityId
)
INSERT INTO member_activity (member_id, activity_id, field1)
VALUES (@m2, @activityId, @field1)
còn SP2:
-- SP2: insert follow
---------------------
IF NOT EXISTS(
SELECT TOP 1 1
FROM follow
WHERE member_id = @memberId AND follower_id = @followerId
)
INSERT INTO follow (member_id, follower_id)
VALUES (@memberId, @followerId)
Chỉnh sửa 2: Sau khi đọc lại các bình luận, tôi nghĩ tôi cũng sẽ thêm một số thông tin về các cột nào là khóa ngoại ...
member_activity.member_id
là một khóa ngoại đối với mộtmember
bảngmember_activity.activity_id
là một khóa ngoại choactivity
bảngfollow.member_id
là một khóa ngoại đối với mộtmember
bảngfollow.follower_id
là một khóa ngoại đối với mộtmember
bảng
Cập nhật 1:
Tôi đã thực hiện một vài thay đổi mà tôi nghĩ có thể giúp ngăn chặn sự bế tắc, không có may mắn.
Những thay đổi tôi đã thực hiện như sau:
-- SP1: insert activity
-----------------------
DECLARE @activityId INT
INSERT INTO activity (field1, field2)
VALUES (@field1, @field2)
SET @activityId = SCOPE_IDENTITY();
MERGE member_activity WITH ( HOLDLOCK ) as target
USING (SELECT @m1 as member_id, @activityId as activity_id, @field1 as field1) as source
ON target.member_id = source.member_id
AND target.activity_id = source.activity_id
WHEN NOT MATCHED THEN
INSERT (member_id, activity_id, field1)
VALUES (source.member_id, source.activity_id, source.field1)
;
MERGE member_activity WITH ( HOLDLOCK ) as target
USING (SELECT @m2 as member_id, @activityId as activity_id, @field1 as field1) as source
ON target.member_id = source.member_id
AND target.activity_id = source.activity_id
WHEN NOT MATCHED THEN
INSERT (member_id, activity_id, field1)
VALUES (source.member_id, source.activity_id, source.field1)
;
và với SP2:
-- SP2: insert follow
---------------------
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION
IF NOT EXISTS(
SELECT TOP 1 1
FROM follow WITH ( UPDLOCK )
WHERE member_id = @memberId AND follower_id = @followerId
)
INSERT INTO follow (member_id, follower_id)
VALUES (@memberId, @followerId)
COMMIT
Với hai thay đổi này, tôi dường như vẫn đang gặp bế tắc.
Nếu có bất cứ điều gì khác tôi có thể cung cấp, xin vui lòng cho tôi biết. Cảm ơn.
SERIALIZABLE
(có nhiều hơn một chút so với điều đó, nhưng đây là một nhận xét không phải là một câu trả lời :)