Dans SQL Server, comment fonctionnent les verrous de lecture?


17

Supposons que j'ai la requête longue durée suivante

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

et supposons que la requête suivante est exécutée pendant l'exécution de la requête ci-dessus

SELECT *
FROM [Table1]

La première requête empêche-t-elle l'exécution de la deuxième requête jusqu'à ce que la première requête soit terminée? Dans l'affirmative, la première requête empêche-t-elle la deuxième requête de s'exécuter sur toutes les lignes ou uniquement sur les lignes impliquées dans la clause WHERE?

ÉDITER:

Supposons que la deuxième requête soit

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements

Réponses:


14

Je vous recommande de lire Comprendre comment SQL Server exécute une requête , il contient une explication du fonctionnement des lectures et des écritures et du fonctionnement du verrouillage.

La vue à 10000 pieds se présente comme suit:

  • les opérateurs de lecture acquièrent des verrous partagés sur les données qu'ils lisent, avant de lire les données
  • les opérateurs d'écriture acquièrent des verrous exclusifs sur les données qu'ils modifient avant de modifier les données
  • les verrous de données ne sont que des chaînes, par exemple. un hachage de la clé en cours de lecture étendue par la base de données et l'objet.
  • le gestionnaire de verrous conserve une liste de tous les verrous accordés et détecte les incompatibilités, selon la matrice de compatibilité des verrous
  • les demandes incompatibles sont suspendues jusqu'à ce que la subvention incompatible les bloquant soit libérée
  • les opérateurs utilisent une hiérarchie de verrous pour déclarer leur intention de lire ou de mettre à jour les données à un niveau supérieur (au niveau de la page ou de la table, en ignorant les options au niveau de la partition). Cela permet aux opérateurs de verrouiller des tables entières sans verrouiller chaque ligne individuelle
  • les verrous de durée de vie et de plage sont utilisés pour appliquer des niveaux d'isolement plus élevés

Ce n'est vraiment que la pointe de l'iceberg. Le sujet est vaste. Dans votre exemple, personne ne peut répondre à votre question sur ce qui est réellement verrouillé car cela dépendra de nombreux facteurs. Bien sûr, aucune application ne devrait émettre un SELECT * FROM Table1 car il manque une clause WHERE et l'utilise *. Ce sont de mauvaises pratiques car, entre autres, elles conduiront exactement à verrouiller les conflits.

Si vous rencontrez des verrous en lecture et en écriture, vous devez examiner la version des lignes et l'isolement des instantanés. Consultez la rubrique Présentation des niveaux d'isolement basés sur le contrôle de version des lignes .


Que faire si j'ai besoin de tout le contenu d'une table (disons que je n'ai que 14 lignes)? Comment est-ce une mauvaise pratique de savoir SELECT * FROM Table1si c'est exactement ce dont j'ai besoin?
Azimuth

1
*en soi est une mauvaise pratique car lorsque la structure de la table change, l'application se casse généralement (des colonnes inattendues apparaissent dans le résultat).
Remus Rusanu

3

Edit: Comme le souligne @MaxVernon , ce qui suit n'est en aucun cas une suggestion d'utiliser NOLOCK , et j'aurais très bien dû mentionner la définition du niveau de transaction READ UNCOMMITEDet laisser la connotation négative se tenir là plutôt que d'évoquer NOLOCKen premier lieu. Donc, comme initialement affiché:

La réponse rapide et simple est "Oui, la première requête bloquera la deuxième requête, sauf si un indice d'index spécifique est spécifié ( NOLOCK , parfois appelé" lecture incorrecte ") ou si le niveau d'isolement des transactions de la deuxième requête est défini sur READ UNCOMMITED(qui fonctionne de manière identique), non."

En réponse aux détails supplémentaires fournis dans la question entraînant l'inclusion d'une WITHclause sur la seconde SELECT, s'excluant mutuellement ou non, les interactions entre les deux requêtes seront largement les mêmes.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

Dans une session distincte, exécutez ce qui suit:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Vous pouvez examiner les verrous actuellement détenus en exécutant sp_lock, de préférence dans une autre session distincte:

EXECUTE dbo.sp_lock;

Vous devriez voir un KEYverrou de type détenu par le spid effectuant la transaction d'insertion en Xmode (exclusif), à ne pas confondre avec les autres IXverrous (Intent-Exclusive). La documentation du verrou indique que le KEYverrouillage est spécifique à la plage, mais il empêche également d'autres transactions d'insérer ou de mettre à jour les colonnes affectées en modifiant les données qui y sont contenues afin qu'elles puissent entrer dans cette plage de la requête d'origine. Le verrou lui-même étant exclusif, la première requête empêche l'accès à la ressource de toute autre transaction simultanée. En effet, toutes les lignes de la colonne sont verrouillées, qu'elles appartiennent ou non à la plage spécifiée par la première requête.

Le Sverrou détenu par la deuxième session sera donc WAITjusqu'à ce que le Xverrou soit effacé, empêchant un autre X(ou U) verrou d'être pris sur cette ressource à partir d'un spid simultané différent avant que la deuxième session ne termine son opération de lecture, justifiant l'existence du Sverrou.

Maintenant, une modification pour plus de clarté: à moins que je ne me trompe dans ce qu'est une lecture sale de la brève description des risques mentionnés ici ... Edit 3 : Je viens de réaliser que je ne considère pas l'effet d'un point de contrôle de fond qui écrit un comme de transaction encore non validée sur le disque, donc oui, mon explication était trompeuse.

Dans la deuxième requête, le premier lot peut (et dans ce cas, renverra) des données non validées. Le deuxième lot, exécuté au niveau d'isolation de transaction par défaut de, READ COMMITEDne sera renvoyé qu'après la fin d'une validation ou d'une restauration dans la première session.

À partir d'ici, vous pouvez consulter vos plans de requête et les niveaux de verrouillage associés, mais mieux encore, vous pouvez lire tout sur les verrous dans SQL Server ici .


1
Un mot d'avertissement sur l'utilisation WITH (NOLOCK)serait utile dans ce cas. Voir brentozar.com/archive/2011/11/… et brentozar.com/archive/2013/02/… pour plus de lecture.
Max Vernon

3
Oh, l' WITH (NOLOCK)indice ne renvoie pas les pages sales de la mémoire qui n'ont pas été validées. Il lit en fait les lignes de la table (qu'elles soient sur disque ou en mémoire cache) sans empêcher les écrivains de mettre à jour ou d'ajouter des lignes aux pages utilisées par la table.
Max Vernon

2
Je suis confus. Si la réponse à "la 1ère requête empêche-t-elle la 2ème de s'exécuter?" est "Non", comment la réponse à la deuxième question peut-elle être "Oui"? Pouvez-vous clarifier les questions auxquelles vous répondez et développer vos réponses?
Jon of All Trades

Modifie à gogo, désolé les gens! Faites-moi savoir s'il y a autre chose qui n'est pas clair!
Avarkx
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.