J'ai une situation où je reçois des blocages, et je pense que j'ai réduit les coupables, mais je ne sais pas trop ce que je peux faire pour y remédier.
Il s'agit d'un environnement de production exécutant SQL Server 2008 R2.
Pour vous donner une vue légèrement simplifiée de la situation:
J'ai 3 tableaux tels que définis ci-dessous:
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,
...
)
La member_activity
table a une clé primaire composée définie comme member_id, activity_id
, car je n'ai besoin que de rechercher des données sur cette table de cette façon.
J'ai également un index non clusterisé sur follow
:
CREATE NONCLUSTERED INDEX [IX_follow_member_id_includes]
ON follow ( member_id ASC ) INCLUDE ( follower_id )
De plus, j'ai une vue liée au schéma network_activity
qui est définie comme suit:
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
Qui a également un index cluster unique:
CREATE UNIQUE CLUSTERED INDEX [IX_network_activity_unique_member_id_activity_id]
ON network_activity
(
member_id ASC,
activity_id ASC
)
Maintenant, j'ai deux procédures stockées bloquées. Ils passent par le processus suivant:
-- 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 (...)
Ces 2 procédures s'exécutent toutes les deux dans l'isolement READ COMMITTED. J'ai réussi à interroger la sortie des événements étendus 1222 et j'ai interprété ce qui suit en ce qui concerne les blocages:
SP1 attend un
RangeS-S
verrou de clé sur l'IX_follow_member_id_includes
index tandis que SP2 détient un verrou (X) en conflitSP2 attend un
S
verrouillage de mode activéPK_member_activity
tandis que SP1 détient un verrou (X) en conflit
Le blocage semble se produire sur la dernière ligne de chaque requête (les insertions). Ce qui n'est pas clair pour moi, c'est pourquoi SP1 veut un verrou sur l' IX_follow-member_id_includes
index. Le seul lien, pour moi, semble provenir de cette vue indexée, c'est pourquoi je l'ai inclus.
Quelle serait la meilleure façon pour moi d'empêcher ces blocages de se produire? Toute aide serait très appréciée. Je n'ai pas beaucoup d'expérience dans la résolution des problèmes de blocage.
Veuillez me faire savoir s'il y a plus d'informations que je peux fournir qui pourraient aider!
Merci d'avance.
Edit 1: Ajout de plus d'informations par demande.
Voici la sortie 1222 de ce blocage:
<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>
Dans ce cas,
associatedObjectId 72057594098679808 correspond à member_activity, PK_member_activity
associatedObjectId 72057594104905728 correspond à follow, IX_follow_member_id_includes
En outre, voici une image plus précise de ce que font SP1 et 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)
également 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: Après avoir relu les commentaires, j'ai pensé ajouter quelques informations sur les colonnes qui sont également des clés étrangères ...
member_activity.member_id
est une clé étrangère vers unemember
tablemember_activity.activity_id
est une clé étrangère de laactivity
tablefollow.member_id
est une clé étrangère vers unemember
tablefollow.follower_id
est une clé étrangère vers unemember
table
Mise à jour 1:
J'ai apporté quelques changements qui, à mon avis, pourraient aider à éviter l'impasse, sans succès.
Les modifications que j'ai apportées étaient les suivantes:
-- 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)
;
et avec 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
Avec ces deux changements, je semble toujours avoir des blocages.
S'il y a autre chose que je peux fournir, faites-le moi savoir. Merci.
SERIALIZABLE
(il y a un peu plus que cela, mais ce n'est pas une réponse :)