Wie funktionieren Lesesperren in SQL Server?

17

Angenommen, ich habe die folgende lange laufende Abfrage

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

Angenommen, die folgende Abfrage wird ausgeführt, während die obige Abfrage ausgeführt wird

SELECT *
FROM [Table1]

Verhindert die erste Abfrage, dass die zweite Abfrage ausgeführt wird, bis die erste Abfrage abgeschlossen ist? Wenn ja, verhindert die erste Abfrage, dass die zweite Abfrage für alle Zeilen oder nur für die in der WHERE-Klausel enthaltenen Zeilen ausgeführt wird?

BEARBEITEN:

Angenommen, die zweite Abfrage lautet

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
cm007
quelle

Antworten:

14

Ich empfehle, dass Sie lesen. Wenn Sie verstehen, wie SQL Server eine Abfrage ausführt, erfahren Sie , wie Lesen und Schreiben funktionieren und wie Sperren funktionieren.

Die 10000ft Ansicht sieht wie folgt aus:

  • Leseoperatoren erwerben gemeinsam genutzte Sperren für die Daten, die sie lesen, bevor sie die Daten lesen
  • Schreiboperatoren erhalten exklusive Sperren für die Daten, die sie ändern, bevor sie die Daten ändern
  • Datensperren sind nur Zeichenfolgen, z. Ein Hash des Schlüssels, der von der Datenbank und dem Objekt gelesen wird.
  • Der Sperrenmanager führt eine Liste aller gewährten Sperren und erkennt Inkompatibilitäten gemäß der Sperren-Kompatibilitätsmatrix
  • Inkompatible Anforderungen werden ausgesetzt, bis die inkompatible Gewährung, die sie blockiert, freigegeben wird
  • Operatoren verwenden eine Sperrhierarchie, um die Absicht zu erklären, Daten auf höherer Ebene zu lesen oder zu aktualisieren (Seiten- oder Tabellenebene, Partitionsebenenoptionen ignorierend). Auf diese Weise können Operatoren ganze Tabellen sperren, ohne jede einzelne Zeile zu sperren
  • Die Sperrenlebensdauer und die Bereichssperren werden verwendet, um höhere Isolationsstufen durchzusetzen

Dies ist wirklich nur die Spitze des Eisbergs. Das Thema ist groß. In Ihrem Beispiel kann niemand Ihre Frage beantworten, was tatsächlich gesperrt wird, da dies von vielen Faktoren abhängt. Natürlich sollte keine Anwendung ein ausgeben, SELECT * FROM Table1 da eine WHERE-Klausel fehlt und verwendet wird *. Dies sind schlechte Praktiken, da sie unter anderem genau zu Sperrenkonflikten führen.

Wenn Sie auf Lese- oder Schreibsperren stoßen, müssen Sie sich mit der Zeilenversionierung und der Snapshot-Isolierung befassen. Lesen Sie Grundlegendes zu zeilenversionsbasierten Isolationsstufen .

Remus Rusanu
quelle
Was ist, wenn ich den gesamten Inhalt einer Tabelle benötige (sagen wir, ich habe nur 14 Zeilen)? Wie ist es schlecht zu üben, SELECT * FROM Table1wenn das genau das ist, was ich brauche?
Azimuth
1
*Dies ist für sich genommen eine schlechte Praxis, da bei Änderungen der Tabellenstruktur die Anwendung normalerweise unterbrochen wird (unerwartete Spalten erscheinen im Ergebnis).
Remus Rusanu
3

Edit: Wie @MaxVernon hervorhebt , ist das Folgende in keiner Weise ein Vorschlag, NOLOCK zu verwenden , und ich hätte auch nur erwähnen sollen, die Transaktionsebene auf zu setzen READ UNCOMMITEDund die negative Konnotation dort stehen zu lassen, als NOLOCKzuerst aufzurufen. So wie ursprünglich gepostet:

Das schnelle und einfache lautet: "Ja, die erste Abfrage blockiert die zweite Abfrage, es sei denn, ein bestimmter Indexhinweis ist angegeben ( NOLOCK , manchmal als" Dirty Read "bezeichnet) oder die Transaktionsisolationsstufe der zweiten Abfrage ist auf READ UNCOMMITED(die identisch funktioniert)." Nein, tut es nicht."

In Reaktion auf die zusätzlichen Details in der Frage, die die Aufnahme einer WITHKlausel in die zweite beinhalten SELECT, die sich gegenseitig ausschließt oder auf andere Weise, werden die Wechselwirkungen zwischen den beiden Abfragen weitgehend gleich sein.

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;

Führen Sie in einer separaten Sitzung Folgendes aus:

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

SELECT  *
FROM    dbo.Foo;

Sie können die aktuell gehaltenen Sperren überprüfen, indem Sie sie ausführen sp_lock, vorzugsweise in einer weiteren separaten Sitzung:

EXECUTE dbo.sp_lock;

Es sollte eine KEYTypensperre angezeigt werden, die von der Spid gehalten wird, die die Einfügetransaktion im X(exklusiven) Modus ausführt , nicht zu verwechseln mit den anderen IX(absichtlich-exklusiven) Sperren. Die Sperrdokumentation gibt an, dass die KEYSperre zwar bereichsspezifisch ist, jedoch auch verhindert, dass andere Transaktionen die betroffenen Spalten einfügen oder aktualisieren, indem die darin enthaltenen Daten so geändert werden, dass sie in diesen Bereich der ursprünglichen Abfrage fallen. Da die Sperrung selbst exklusiv ist, verhindert die erste Abfrage den Zugriff auf die Ressource durch eine andere gleichzeitige Transaktion. Tatsächlich sind alle Zeilen der Spalte gesperrt, unabhängig davon, ob sie in den von der ersten Abfrage angegebenen Bereich fallen oder nicht.

Die SSperre, die von der zweiten Sitzung gehalten wird, bleibt so lange bestehen, WAITbis die XSperre aufgehoben wird, wodurch verhindert wird, dass eine andere X(oder eine andere U) Sperre für diese Ressource von einer anderen gleichzeitigen Spid übernommen wird, bevor die zweite Sitzung ihre Leseoperation abgeschlossen hat, was die Existenz der SSperre rechtfertigt .

Jetzt eine Bearbeitung für Klarheit: Wenn ich mich nicht irre, was ein zerstörendes Lesen ist aus der kurzen Beschreibung der hier genannten Risiken ... Bearbeiten 3 : Ich habe realisiert Ich betrachte nicht die Wirkung eines Hintergrund - Checkpoint , der schreibt eine als noch nicht festgeschriebene Transaktion auf Festplatte, also ja, meine Erklärung war irreführend.

In der zweiten Abfrage kann (und wird in diesem Fall) der erste Stapel nicht festgeschriebene Daten zurückgeben. Der zweite Stapel, der in der Standardtransaktionsisolationsstufe von ausgeführt READ COMMITEDwird, wird erst nach Abschluss eines Commits oder Rollbacks in der ersten Sitzung zurückgegeben.

Von hier aus können Sie Ihre Abfragepläne aussehen und die damit verbundenen Sperre Ebenen, aber noch besser, können Sie alles über Schlösser in SQL Server lesen Sie hier .

Avarkx
quelle
1
In WITH (NOLOCK)diesem Fall wäre eine Warnung zur Verwendung hilfreich. Weitere Informationen finden Sie unter brentozar.com/archive/2011/11/… und brentozar.com/archive/2013/02/… .
Max Vernon
3
Oh, der WITH (NOLOCK)Hinweis gibt keine schmutzigen Seiten aus dem Speicher zurück, die nicht festgeschrieben wurden. Tatsächlich werden die Zeilen aus der Tabelle gelesen (ob auf der Festplatte oder im Arbeitsspeicher zwischengespeichert), ohne dass die Writer daran gehindert werden, die von der Tabelle verwendeten Seiten zu aktualisieren oder Zeilen hinzuzufügen.
Max Vernon
2
Ich bin verwirrt. Wenn die Antwort auf "Verhindert die erste Abfrage, dass die zweite ausgeführt wird?" ist "Nein", wie kann die Antwort auf die zweite Frage "Ja" sein? Können Sie klären, welche Fragen Sie beantworten, und Ihre Antworten erweitern?
Jon of All Trades
Bearbeitungen in Hülle und Fülle, sorry Leute! Lassen Sie mich wissen, wenn noch etwas unklar ist!
Avarkx