Kilitlenme yaşadığım bir durumum var ve sanırım suçluları daralttım, ama düzeltmek için ne yapabileceğimden tam olarak emin değilim.
Bu, SQL Server 2008 R2 çalıştıran bir üretim ortamındadır.
Size durumun biraz basitleştirilmiş bir görünümünü vermek için:
Aşağıda tanımlandığı gibi 3 tablo var:
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,
...
)
member_activity
Tablo Birincil Anahtar olarak tanımlanan bir bileşik vardır member_id, activity_id
sadece hiç o masaya yolda olduğunu verileri aramak gerekir, çünkü.
Ayrıca üzerinde kümelenmemiş bir dizin var follow
:
CREATE NONCLUSTERED INDEX [IX_follow_member_id_includes]
ON follow ( member_id ASC ) INCLUDE ( follower_id )
Ayrıca, network_activity
aşağıdaki gibi tanımlanan bir Şemaya bağlı görünüm var:
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
Ayrıca, benzersiz bir kümelenmiş dizin vardır:
CREATE UNIQUE CLUSTERED INDEX [IX_network_activity_unique_member_id_activity_id]
ON network_activity
(
member_id ASC,
activity_id ASC
)
Şimdi, kilitlenmemiş iki saklı yordam var. Aşağıdaki süreçten geçerler:
-- 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 (...)
Bu 2 yordamın her ikisi de READ COMMITTED izolasyonunda çalışır. Ben 1222 genişletilmiş olaylar çıktı sorgulamayı başardım ve çıkmazlar ile ilgili olarak aşağıdaki yorum:
SP2 çakışan (X) bir kilidi tutarken SP1 dizinde bir
RangeS-S
tuş kilidi bekliyorIX_follow_member_id_includes
SP1 çakışan (X) bir kilidi tutarken SP2 bir
S
mod kilidini bekliyorPK_member_activity
Kilitlenmenin her sorgunun son satırında (ekler) gerçekleştiği görülüyor. Benim için belirsiz olan şey, SP1'in neden IX_follow-member_id_includes
endeks üzerinde kilit istemesi . Bana göre tek bağlantı bu dizinli görünümden gibi görünüyor, bu yüzden dahil ettim.
Bu kilitlenmelerin olmasını önlememin en iyi yolu ne olurdu? Herhangi bir yardım çok takdir edilecektir. Kilitlenme sorunlarını çözme konusunda fazla deneyimim yok.
Yardımcı olabileceğim daha fazla bilgi varsa lütfen bize bildirin!
Şimdiden teşekkürler.
Düzenleme 1: İstek başına daha fazla bilgi ekleme.
İşte bu çıkmazdan 1222 çıktı:
<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>
Bu durumda,
relatedObjectId 72057594098679808 karşılık gelir member_activity, PK_member_activity
relatedObjectId 72057594104905728 karşılık gelir follow, IX_follow_member_id_includes
Ayrıca, SP1 ve SP2'nin ne yaptığına dair daha kesin bir resim
-- 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)
ayrıca 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)
Edit 2: Yorumları tekrar okuduktan sonra, hangi sütunların yabancı anahtarlar olduğu hakkında bazı bilgiler ekleyeceğimi düşündüm ...
member_activity.member_id
birmember
tablonun yabancı anahtarıdırmember_activity.activity_id
activity
masanın yabancı anahtarıfollow.member_id
birmember
tablonun yabancı anahtarıdırfollow.follower_id
birmember
tablonun yabancı anahtarıdır
Güncelleme 1:
Kilitlenmeyi önlemeye yardımcı olabileceğini düşündüğüm birkaç değişiklik yaptım, şanssız.
Yaptığım değişiklikler şöyle:
-- 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)
;
ve SP2 ile:
-- 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
Bu iki değişiklikle birlikte hala kilitlenmeler görüyorum.
Verebileceğim başka bir şey varsa lütfen bana bildirin. Teşekkürler.
SERIALIZABLE
(bundan biraz daha fazlası vardır, ancak bu bir yanıt değildir :)