Confus à propos de UPDLOCK, HOLDLOCK

88

Lors de la recherche sur l'utilisation des conseils de table mes , je suis tombé sur ces deux questions:

Les réponses aux deux questions indiquent que lors de l'utilisation (UPDLOCK, HOLDLOCK) , d'autres processus ne pourront pas lire les données de cette table, mais je n'ai pas vu cela. Pour tester, j'ai créé une table et démarré deux fenêtres SSMS. À partir de la première fenêtre, j'ai exécuté une transaction qui a sélectionné dans la table en utilisant divers indices de table. Pendant que la transaction était en cours d'exécution, à partir de la deuxième fenêtre, j'ai exécuté diverses instructions pour voir lesquelles seraient bloquées.

La table de test:

CREATE TABLE [dbo].[Test](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Value] [nvarchar](50) NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Depuis la fenêtre SSMS 1:

BEGIN TRANSACTION

SELECT * FROM dbo.Test WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'

COMMIT TRANSACTION

À partir de la fenêtre SSMS 2 (a exécuté l'une des opérations suivantes):

SELECT * FROM dbo.Test
INSERT dbo.Test(Value) VALUES ('bar')
UPDATE dbo.Test SET Value = 'baz' WHERE Value = 'bar'
DELETE dbo.Test WHERE Value= 'baz'

Effet des différentes indications de table sur les instructions exécutées dans la fenêtre 2:

           (UPDLOCK)       (HOLDLOCK)    (UPDLOCK, HOLDLOCK)    (TABLOCKX)
---------------------------------------------------------------------------
SELECT    not blocked      not blocked       not blocked         blocked
INSERT    not blocked        blocked           blocked           blocked
UPDATE      blocked          blocked           blocked           blocked
DELETE      blocked          blocked           blocked           blocked

Ai-je mal compris les réponses données à ces questions ou ai-je commis une erreur lors de mes tests? Sinon, pourquoi utiliseriez-vous (UPDLOCK, HOLDLOCK)vs (HOLDLOCK)seul?


Explication supplémentaire de ce que j'essaie d'accomplir:

Je voudrais sélectionner des lignes dans une table et empêcher les données de cette table d'être modifiées pendant que je la traite. Je ne modifie pas ces données et je souhaite autoriser les lectures.

Cette réponse dit clairement que (UPDLOCK, HOLDLOCK)cela bloquera les lectures (pas ce que je veux). Les commentaires sur cette réponse impliquent que c'est cela HOLDLOCKqui empêche les lectures. Pour essayer de mieux comprendre les effets des indices de table et voir si UPDLOCKseul ferait ce que je voulais, j'ai fait l'expérience ci-dessus et j'ai obtenu des résultats qui contredisent ces réponses.

Actuellement, je pense que (HOLDLOCK)c'est ce que je devrais utiliser, mais je crains d'avoir commis une erreur ou oublié quelque chose qui reviendra me mordre à l'avenir, d'où cette question.

Jeff Ogata
la source

Réponses:

102

Pourquoi UPDLOCK bloquerait-il les sélections? La matrice de compatibilité de verrouillage montre clairement Npour le conflit S / U et U / S, comme dans No Conflict .

Quant à l' indice HOLDLOCK, la documentation déclare:

HOLDLOCK: équivaut à SERIALIZABLE. Pour plus d'informations, consultez SERIALIZABLE plus loin dans cette rubrique.

...

SERIALIZABLE: ... Le scan est effectué avec la même sémantique qu'une transaction exécutée au niveau d'isolement SERIALIZABLE ...

et la rubrique Niveau d'isolement des transactions explique ce que signifie SERIALIZABLE:

Aucune autre transaction ne peut modifier les données qui ont été lues par la transaction en cours jusqu'à ce que la transaction en cours se termine.

Les autres transactions ne peuvent pas insérer de nouvelles lignes avec des valeurs de clé qui tomberaient dans la plage de clés lues par les instructions de la transaction en cours jusqu'à ce que la transaction en cours se termine.

Par conséquent, le comportement que vous voyez est parfaitement expliqué par la documentation du produit:

  • UPDLOCK ne bloque pas SELECT ni INSERT simultanés, mais bloque toute UPDATE ou DELETE des lignes sélectionnées par T1
  • HOLDLOCK signifie SERALIZABLE et autorise donc les SELECTS, mais bloque UPDATE et DELETES des lignes sélectionnées par T1, ainsi que tout INSERT dans la plage sélectionnée par T1 (qui est la table entière, donc toute insertion).
  • (UPDLOCK, HOLDLOCK): votre expérience ne montre pas ce qui bloquerait en plus du cas ci-dessus, à savoir une autre transaction avec UPDLOCK en T2 :
    SELECT * FROM dbo.Test WITH (UPDLOCK) WHERE ...
  • TABLOCKX pas besoin d'explications

La vraie question est qu'est - ce que vous essayez de réaliser ? Jouer avec des indices de verrouillage sans une compréhension absolue à 110% de la sémantique de verrouillage est synonyme de problèmes ...

Après la modification de l'OP:

Je voudrais sélectionner des lignes dans une table et empêcher les données de cette table d'être modifiées pendant que je la traite.

Vous devez utiliser l'un des niveaux d'isolation des transactions les plus élevés. REPEATABLE READ empêchera les données que vous lisez d'être modifiées. SERIALIZABLE empêchera les données que vous lisez d'être modifiées et de nouvelles données d'être insérées. L'utilisation des niveaux d'isolation des transactions est la bonne approche, par opposition à l'utilisation d'indices de requête. Kendra Little a une belle affiche expliquant les niveaux d'isolement .

Remus Rusanu
la source
+1, et merci pour la réponse détaillée. Je mettrai à jour ma question pour ajouter le détail de mon objectif.
Jeff Ogata
1
@Remus Rusanu pourriez-vous s'il vous plaît expliquer pourquoi la bonne approche utilise des niveaux d'isolement plutôt que des conseils de requête? J'ai une procédure dans laquelle je n'ai besoin que de verrouiller deux tables contre la modification et j'utilise TABLOCK, HOLDLOCK, dois-je vraiment passer au niveau d'isolement et verrouiller toutes les tables dans ma transaction?
Steve
Je pourrais aimer une explication pour TABLOCKX :)
niico
Remarque: Le lien vers l'entrée de blog pour Kendra Little renvoie un 404. Je ne trouve aucune entrée datée du 2 février 2011, comme le lien le suggère.
Bacon Bits
21

UPDLOCK est utilisé lorsque vous souhaitez verrouiller une ou plusieurs lignes pendant une instruction SELECT pour une instruction de mise à jour future. La future mise à jour pourrait être la toute prochaine déclaration de la transaction.

Les autres sessions peuvent toujours voir les données. Ils ne peuvent tout simplement pas obtenir des verrous incompatibles avec UPDLOCK et / ou HOLDLOCK.

Vous utilisez UPDLOCK lorsque vous souhaitez empêcher les autres sessions de modifier les lignes que vous avez verrouillées. Cela limite leur capacité à mettre à jour ou à supprimer des lignes verrouillées.

Vous utilisez HOLDLOCK lorsque vous souhaitez empêcher les autres sessions de modifier les données que vous consultez. Cela limite leur capacité à insérer, mettre à jour ou supprimer les lignes que vous avez verrouillées. Cela vous permet d'exécuter à nouveau la requête et de voir les mêmes résultats.

Scott Bruns
la source
1
Merci, mais je ne pense pas que vous ayez vraiment répondu à ma question: les réponses à ces questions étaient-elles erronées en déclarant ce (UPDLOCK,HOLDLOCK)bloc de lecture, et y a-t-il une raison d'utiliser (UPDLOCK,HOLDLOCK)au lieu de simplement (HOLDLOCK)?
Jeff Ogata
Ma deuxième déclaration répond à votre question, ils sont faux. Les autres sessions peuvent toujours lire les données.
Scott Bruns
Updlock, Holdlock n'est pas la même chose que holdlock. Updlock, holdlock verrouille les lignes pour la mise à jour et sérialise votre transaction. Holdlock en lui-même sérialise simplement votre transaction. Il ne verrouille pas les lignes sélectionnées pour un accès ultérieur.
Scott Bruns
"UPDLOCK est utilisé lorsque vous souhaitez verrouiller une ou plusieurs lignes pendant une instruction de sélection pour une future instruction de mise à jour." J'adore ça, car XLOCK peut ne pas fonctionner parfois
Yiping