Quelles sont mes options de verrouillage pour une instruction de fusion?

13

J'ai une procédure stockée qui exécute une MERGEinstruction .

Il semble qu'il verrouille la table entière par défaut lors de la fusion.

J'appelle cette procédure stockée à l'intérieur d'une transaction où je fais également d'autres choses et je souhaite que cela ne verrouille que les lignes affectées.

J'ai essayé l'indice MERGE INTO myTable WITH (READPAST)et il semblait se verrouiller moins. Mais il y avait un avertissement dans le ms doc qui disait qu'il pouvait insérer des clés en double, contournant même la clé primaire.

Voici mon schéma de table:

CREATE TABLE StudentDetails
(
StudentID INTEGER PRIMARY KEY,
StudentName VARCHAR(15)
)
GO
INSERT INTO StudentDetails
VALUES(1,'WANG')
INSERT INTO StudentDetails
VALUES(2,'JOHNSON')
GO

CREATE TABLE StudentTotalMarks
(
Id INT IDENTITY PRIMARY KEY,
StudentID INTEGER REFERENCES StudentDetails,
StudentMarks INTEGER
)
GO
INSERT INTO StudentTotalMarks
VALUES(1,230)
INSERT INTO StudentTotalMarks
VALUES(2,255)
GO

Voici ma procédure stockée:

CREATE PROCEDURE MergeTest 
    @StudentId int,
    @Mark int
AS  

WITH Params
AS
(
    SELECT @StudentId as StudentId,
        @Mark as Mark
)
    MERGE StudentTotalMarks AS stm
    USING Params p
    ON stm.StudentID = p.StudentId
    WHEN MATCHED AND stm.StudentMarks > 250 THEN DELETE
    WHEN MATCHED THEN UPDATE SET stm.StudentMarks = p.Mark
    WHEN NOT MATCHED THEN
        INSERT(StudentID,StudentMarks)
        VALUES(p.StudentId, p.Mark);
GO

Voici comment j'observe le verrouillage:

begin tran
EXEC MergeTest 1, 1

Et puis dans une autre session:

EXEC MergeTest 2, 2

La deuxième session attend la fin de la première avant de continuer.

John Buchanan
la source
1
WITH (READPAST)indique à SQL Server d'ignorer simplement les lignes verrouillées par d'autres sessions. Êtes-vous sûr de vouloir faire ça? De plus, combien de lignes de ce tableau modifiez-vous? Montrez-nous le schéma de la table (y compris les index) et l' MERGEinstruction que vous exécutez.
Nick Chammas
@NickChammas merci pour l'aide, j'ai mis à jour la question avec les détails. J'imagine que READPAST serait mauvais ...
John Buchanan

Réponses:

12

Vous devez donner au processeur de requêtes un chemin d'accès plus efficace pour localiser les StudentTotalMarksenregistrements. Comme écrit, la requête nécessite une analyse complète de la table avec un prédicat résiduel [StudentID] = [@StudentId]appliqué à chaque ligne:

Plan de numérisation

Le moteur prend U(met à jour) les verrous lors de la lecture comme défense de base contre une cause courante de blocages de conversion. Ce comportement signifie que les seconds blocs d'exécution lorsque vous essayez d'obtenir un Uverrou sur la ligne déjà verrouillée avec un Xverrou (exclusif) par la première exécution.

L'index suivant fournit un meilleur chemin d'accès, évitant de prendre des Uverrous inutiles :

CREATE UNIQUE INDEX uq1 
ON dbo.StudentTotalMarks (StudentID) 
INCLUDE (StudentMarks);

Le plan de requête inclut désormais une opération de recherche StudentID = [@StudentId], les Uverrous ne sont donc demandés que sur les lignes cibles:

Plan de recherche

Il n'est pas nécessaire que l' index soit UNIQUEpour résoudre le problème en question (bien qu'il INCLUDEsoit nécessaire pour en faire un index de couverture pour cette requête).

Faire StudentIDle PRIMARY KEYdu StudentTotalMarkstableau résoudrait aussi le problème de chemin d'accès (et apparemment redondant Idpourrait être retiré colonne). Vous devez toujours appliquer des clés alternatives avec une contrainte UNIQUEou PRIMARY KEY(et éviter d'ajouter des clés de substitution sans signification sans raison valable).

Paul White 9
la source