Ho una situazione in cui sto ottenendo blocchi, e penso di aver ristretto i colpevoli, ma non sono sicuro di cosa posso fare per risolverlo.
Questo si trova in un ambiente di produzione che esegue SQL Server 2008 R2.
Per darti una visione leggermente semplificata della situazione:
Ho 3 tabelle come definito di seguito:
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,
...
)
Il member_activity
tabella ha una chiave primaria composta definita come member_id, activity_id
, perché ho sempre e solo bisogno di cercare i dati su quella tabella in quel modo.
Ho anche un indice non cluster su follow
:
CREATE NONCLUSTERED INDEX [IX_follow_member_id_includes]
ON follow ( member_id ASC ) INCLUDE ( follower_id )
Inoltre, ho una vista associata allo schema network_activity
definita come segue:
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
Che ha anche un indice cluster univoco:
CREATE UNIQUE CLUSTERED INDEX [IX_network_activity_unique_member_id_activity_id]
ON network_activity
(
member_id ASC,
activity_id ASC
)
Ora, ho due stored procedure bloccate. Passano attraverso il seguente processo:
-- 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 (...)
Queste 2 procedure vengono eseguite entrambe in isolamento READ COMMITTED. Sono riuscito a interrogare l'output degli eventi estesi 1222 e ho interpretato quanto segue in relazione ai deadlock:
SP1 è in attesa di un
RangeS-S
blocco tastiIX_follow_member_id_includes
sull'indice mentre SP2 contiene un blocco in conflitto (X)SP2 è in attesa di un
S
blocco della modalitàPK_member_activity
mentre SP1 ha un blocco in conflitto (X)
Il deadlock sembra verificarsi nell'ultima riga di ogni query (gli inserti). Ciò che non mi è chiaro è perché SP1 desidera un blocco IX_follow-member_id_includes
sull'indice. L'unico link, per me, sembra provenire da questa vista indicizzata ed è per questo che l'ho incluso.
Quale sarebbe il modo migliore per me per evitare che si verifichino questi deadlock? Qualsiasi aiuto sarebbe molto apprezzato. Non ho molta esperienza nella risoluzione dei problemi di deadlock.
Per favore fatemi sapere se ci sono ulteriori informazioni che posso fornire che potrebbero aiutare!
Grazie in anticipo.
Modifica 1: aggiunta di ulteriori informazioni per richiesta.
Ecco l'output 1222 da questo deadlock:
<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>
In questo caso,
associatedObjectId 72057594098679808 corrisponde a member_activity, PK_member_activity
associatedObjectId 72057594104905728 corrisponde a follow, IX_follow_member_id_includes
Inoltre, ecco un'immagine più precisa di ciò che stanno facendo SP1 e SP2
-- 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)
anche 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)
Modifica 2: Dopo aver riletto i commenti, ho pensato di aggiungere alcune informazioni su quali colonne sono anche le chiavi esterne ...
member_activity.member_id
è una chiave esterna per unamember
tabellamember_activity.activity_id
è una chiave esterna per laactivity
tabellafollow.member_id
è una chiave esterna per unamember
tabellafollow.follower_id
è una chiave esterna per unamember
tabella
Aggiornamento 1:
Ho apportato un paio di modifiche che pensavo potessero aiutare a prevenire lo stallo, senza fortuna.
Le modifiche che ho apportato sono state le seguenti:
-- 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)
;
e con 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
Con questi due cambiamenti, sembra che stia ancora ottenendo deadlock.
Se c'è qualcos'altro che posso fornire, per favore fatemelo sapere. Grazie.
SERIALIZABLE
(c'è un po 'di più rispetto a quello, ma questo è un commento non una risposta :)