Lock Eskalation - Was passiert hier?

137

Beim Ändern einer Tabelle (Entfernen einer Spalte) in SQL Server 2008 habe ich auf die Schaltfläche Änderungsskript generieren geklickt und festgestellt, dass das generierte Änderungsskript die Spalte löscht, "go" sagt und dann eine zusätzliche ALTER TABLE-Anweisung ausführt, die anscheinend festgelegt wird die Sperreneskalation für die Tabelle auf "TABELLE". Beispiel:

ALTER TABLE dbo.Contract SET (LOCK_ESCALATION = TABLE)

Ich sollte auch beachten, dass dies das letzte ist, was das Änderungsskript tut. Was macht es hier und warum setzt es LOCK_ESCALATION auf TABLE?

James Alexander
quelle

Antworten:

164

Mit " Eskalation sperren" behandelt SQL das Sperren für große Updates. Wenn SQL viele Zeilen ändern wird, ist es für das Datenbankmodul effizienter, weniger größere Sperren (z. B. die gesamte Tabelle) zu verwenden, anstatt viele kleinere Dinge (z. B. Zeilensperren) zu sperren.

Dies kann jedoch problematisch sein, wenn Sie eine große Tabelle haben, da eine Sperre für die gesamte Tabelle andere Abfragen für lange Zeit sperren kann. Das ist der Kompromiss: Viele Sperren mit kleiner Granularität sind langsamer als weniger (oder eine) grobkörnige Sperren. Wenn mehrere Abfragen verschiedene Teile einer Tabelle sperren, besteht die Möglichkeit eines Deadlocks, wenn ein Prozess auf einen anderen wartet.

Es gibt eine LOCK_ESCALATIONneue Option auf Tabellenebene in SQL 2008, mit der die Eskalation von Sperren gesteuert werden kann. Die Standardeinstellung "TABLE" ermöglicht, dass Sperren bis zur Tabellenebene eskalieren. DISABLE verhindert in den meisten Fällen eine Eskalation der Sperre für die gesamte Tabelle. AUTO erlaubt Tabellensperren, außer wenn die Tabelle partitioniert ist. In diesem Fall werden Sperren nur bis zur Partitionsebene vorgenommen. Weitere Informationen finden Sie in diesem Blogbeitrag .

Ich vermute, dass die IDE diese Einstellung beim erneuten Erstellen einer Tabelle hinzufügt, da TABLE in SQL 2008 die Standardeinstellung ist. Beachten Sie, dass LOCK_ESCALATION in SQL 2005 nicht unterstützt wird. Sie müssen sie daher entfernen, wenn Sie versuchen, das Skript auf einem auszuführen Instanz 2005. Da TABLE die Standardeinstellung ist, können Sie diese Zeile auch sicher entfernen, wenn Sie Ihr Skript erneut ausführen.

Beachten Sie auch, dass in SQL 2005, bevor diese Einstellung vorhanden war, alle Sperren auf Tabellenebene eskalieren konnten. Mit anderen Worten, "TABLE" war die einzige Einstellung in SQL 2005.

Justin Grant
quelle
1
Doppelter Beitrag auch in den MSDN-Foren: social.msdn.microsoft.com/Forums/en-US/sqldatabaseengine/thread/…
Jonathan Kehayias
6
@dma_k - Diese Option ist nicht relevant, CREATE TABLEda die Tabelle noch nicht vorhanden ist und daher nichts gesperrt werden muss.
Justin Grant
1
Aber warum steht die Anweisung LOCK_ESCALATION nach der ersten Anweisung ALTER TABLE im Änderungsskript, wenn eine Tabelle in SSMS entworfen wird? Sicherlich ist die Arbeit zu diesem Zeitpunkt bereits erledigt. Sollte es nicht sein, bevor die Struktur der Tabelle geändert wird?
Umgekehrter Ingenieur
2
@ DaveBoltman - Das SET ist Teil der ALTER TABLE-Anweisung. Es ist keine separate Aussage. Siehe docs.microsoft.com/en-us/sql/t-sql/statements/…
Justin Grant
2
JustinGrant, dennoch steht die Frage von @DaveBoltman. Das Skript, das SSMS beispielsweise zum Hinzufügen einer neuen Spalte generiert, verfügt über zwei separate ALTER TABLEAnweisungen. Erst ALTER TABLE ADD columndann GO, dann zweitens ALTER TABLE SET LOCK_ESCALATION=TABLE, dann zweitens GO. Wird LOCK_ESCALATIONalso festgelegt, nachdem die Spalte hinzugefügt wurde. Was bringt es, es nachträglich einzustellen? Diese beiden ALTER TABLEAnweisungen werden in eine Transaktion eingeschlossen, aber die Spalte wird noch hinzugefügt, bevor die LOCK_ESCALATIONfestgelegt wird. Ich denke, ich werde ein bisschen weiter graben und eine andere Antwort schreiben.
Vladimir Baranov
11

Sie können überprüfen, ob Sie die Anweisung LOCK_ESCALATION in Ihr Skript aufnehmen müssen, indem Sie diesen Wert vor und nach dem Ausführen des Hauptteils Ihres Skripts vergleichen:

SELECT lock_escalation_desc FROM sys.tables WHERE name='yourtablename'

In meinem Fall scheint das Ändern der Tabelle zum Löschen oder Hinzufügen einer Einschränkung diesen Wert nicht zu ändern.

Bogdan Verbenets
quelle
11

Die Antwort von Justin Grant erklärt, was die LOCK_ESCALATIONEinstellung im Allgemeinen bewirkt, übersieht jedoch ein wichtiges Detail und erklärt nicht, warum SSMS den Code generiert, der sie festlegt. Insbesondere sieht es sehr seltsam aus, dass das LOCK_ESCALATIONals letzte Anweisung im Skript gesetzt ist.

Ich habe nur wenige Tests durchgeführt und hier ist mein Verständnis dafür, was hier passiert.

Kurzfassung

Die ALTER TABLEAnweisung, die implizit eine Spalte hinzufügt, löscht oder ändert, erfordert eine SCH-M-Sperre (Schema Modify) für die Tabelle, die nichts mit der LOCK_ESCALATIONEinstellung einer Tabelle zu tun hat . LOCK_ESCALATIONwirkt sich das Verhalten während der DML - Anweisungen Sperren ( INSERT, UPDATE, DELETE, etc.), nicht während der DDL - Anweisungen ( ALTER). Die SCH-M-Sperre ist immer eine Sperre des gesamten Datenbankobjekts, in diesem Beispiel der Tabelle.

Dies ist wahrscheinlich, woher die Verwirrung kommt.

SSMS fügt die ALTER TABLE <TableName> SET (LOCK_ESCALATION = ...)Anweisung in allen Fällen zu seinem Skript hinzu , auch wenn sie nicht benötigt wird. In den Fällen , wenn diese Anweisung benötigt wird, wird es hinzugefügt , um die aktuelle Einstellung der Tabelle zu erhalten, nicht um die Tabelle zu sperren in irgendeiner bestimmten Weise während der Änderung der Tabelle Schema , das in diesem Skript passiert.

Mit anderen Worten, die Tabelle wird bei der ersten ALTER TABLE ALTER COLUMNAnweisung mit der SCH-M-Sperre gesperrt, während die gesamte Arbeit zum Ändern des Tabellenschemas erledigt ist. Die letzte ALTER TABLE SET LOCK_ESCALATIONAussage hat keinen Einfluss darauf. Es wirkt sich nur Aussagen über zukünftige DML ( INSERT, UPDATE, DELETE, etc.) für diese Tabelle.

Auf den ersten Blick sieht es so aus, als hätte SET LOCK_ESCALATION = TABLEes etwas damit zu tun, dass wir die gesamte Tabelle ändern (wir ändern hier ihr Schema), aber es ist irreführend.

Lange Version

Wenn Sie die Tabelle in einigen Fällen ändern, generiert SSMS ein Skript, das die gesamte Tabelle neu erstellt, und in einigen einfacheren Fällen (z. B. Hinzufügen oder Löschen einer Spalte) erstellt das Skript die Tabelle nicht neu.

Nehmen wir diese Beispieltabelle als Beispiel:

CREATE TABLE [dbo].[Test](
    [ID] [int] NOT NULL,
    [Col1] [nvarchar](50) NOT NULL,
    [Col2] [int] NOT 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]
GO

Jede Tabelle verfügt über eine LOCK_ESCALATIONEinstellung, die TABLEstandardmäßig festgelegt ist. Lassen Sie es uns hier ändern:

ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)

Wenn ich nun versuche, den Col1Typ im SSMS-Tabellen-Designer zu ändern, generiert SSMS ein Skript, das die gesamte Tabelle neu erstellt:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_Test
    (
    ID int NOT NULL,
    Col1 nvarchar(10) NOT NULL,
    Col2 int NOT NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_Test SET (LOCK_ESCALATION = DISABLE)
GO
IF EXISTS(SELECT * FROM dbo.Test)
     EXEC('INSERT INTO dbo.Tmp_Test (ID, Col1, Col2)
        SELECT ID, CONVERT(nvarchar(10), Col1), Col2 FROM dbo.Test WITH (HOLDLOCK TABLOCKX)')
GO
DROP TABLE dbo.Test
GO
EXECUTE sp_rename N'dbo.Tmp_Test', N'Test', 'OBJECT' 
GO
ALTER TABLE dbo.Test ADD CONSTRAINT
    PK_Test PRIMARY KEY CLUSTERED 
    (
    ID
    ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT

Sie können oben sehen, dass es LOCK_ESCALATIONfür die neu erstellte Tabelle festgelegt wird. SSMS tut dies, um die aktuelle Einstellung der Tabelle beizubehalten. SSMS generiert diese Zeile, auch wenn der aktuelle Wert der Einstellung der Standardwert TABLEist. Nur um sicher und explizit zu sein und mögliche zukünftige Probleme zu vermeiden, wenn sich diese Standardeinstellung in Zukunft ändert, denke ich. Das macht Sinn.

In diesem Beispiel ist es wirklich erforderlich, die SET LOCK_ESCALATIONAnweisung zu generieren , da die Tabelle neu erstellt wird und ihre Einstellung beibehalten werden muss.

Wenn ich versuche, mithilfe des SSMS-Tabellen-Designers eine einfache Änderung an der Tabelle vorzunehmen, z. B. eine neue Spalte hinzuzufügen, generiert SSMS ein Skript, das die Tabelle nicht neu erstellt:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Test ADD
    NewCol nchar(10) NULL
GO
ALTER TABLE dbo.Test SET (LOCK_ESCALATION = DISABLE)
GO
COMMIT

Wie Sie sehen können, wird die ALTER TABLE SET LOCK_ESCALATIONAnweisung trotzdem hinzugefügt , obwohl sie in diesem Fall überhaupt nicht benötigt wird. Die erste ALTER TABLE ... ADDändert die aktuelle Einstellung nicht. Ich denke, SSMS-Entwickler haben entschieden, dass es sich nicht lohnt, festzustellen, in welchen Fällen diese ALTER TABLE SET LOCK_ESCALATIONAnweisung redundant ist, und sie immer zu generieren, nur um sicher zu gehen. Es schadet nicht, diese Aussage jedes Mal hinzuzufügen.

Auch hier ist die tabellenweite LOCK_ESCALATIONEinstellung irrelevant, während sich das Tabellenschema über die ALTER TABLEAnweisung ändert . LOCK_ESCALATIONDie Einstellung wirkt sich nur auf das Sperrverhalten von DML-Anweisungen aus, z UPDATE.

Zum Schluss ein Zitat aus ALTER TABLE, betonen Sie meins:

Die in ALTER TABLE angegebenen Änderungen werden sofort implementiert. Wenn die Änderungen Änderungen an den Zeilen in der Tabelle erfordern, aktualisiert ALTER TABLE die Zeilen. ALTER TABLE erhält eine SCH-M-Sperre (Schema Modify) für die Tabelle, um sicherzustellen, dass während der Änderung keine anderen Verbindungen auf die Metadaten für die Tabelle verweisen, mit Ausnahme von Online-Indexoperationen, die am Ende eine sehr kurze SCH-M-Sperre erfordern. Bei einer Operation ALTER TABLE… SWITCH wird die Sperre sowohl für die Quell- als auch für die Zieltabelle erfasst. Die an der Tabelle vorgenommenen Änderungen werden protokolliert und können vollständig wiederhergestellt werden. Änderungen, die sich auf alle Zeilen in sehr großen Tabellen auswirken, z. B. das Löschen einer Spalte oder das Hinzufügen einer NOT NULL-Spalte mit einem Standardwert in einigen Editionen von SQL Server, können lange dauern, bis viele Protokolldatensätze abgeschlossen und generiert sind. Diese ALTER TABLE-Anweisungen sollten mit der gleichen Sorgfalt ausgeführt werden wie alle INSERT-, UPDATE- oder DELETE-Anweisungen, die viele Zeilen betreffen.

Vladimir Baranov
quelle