Themenseite · SQL Server Locking

Optimized Locking einschalten? Erst messen, dann entscheiden

Acht Sitzungen ändern dieselbe Tabelle, jede ihre eigene Gruppe von Zeilen. Ohne passenden Index blockieren sie sich trotzdem gegenseitig. Mit Optimized Locking verschwindet das Warten, der Durchsatz steigt in unserem Lab von 69 auf 129 Änderungen je Sekunde. Ein Erfolg. Dann geben wir derselben Tabelle den passenden Schlüssel, ganz ohne Optimized Locking: 386 Änderungen je Sekunde, bei einem Sechstel der CPU.

Stand 11.11.2026 · ca. 13 Minuten

Thema PoC · Messung · ADR-KostenSQL-Abfrage 1Lab SQL Server 2025 CU10
Acht Schreiber, jeder ändert seine eigenen Zeilenohne passenden Index69,3 Änderungen/sohne Index, mit Optimized Locking128,7 Änderungen/spassender Schlüssel, ohne Optimized Locking386,0 Änderungen/sMedian aus drei Läufen à 30 s, gemessen im PSG-Lab auf SQLServer 2025 CU10.

Optimized Locking löst einen bestimmten Konflikt sehr gut. Die Frage vor dem Einschalten lautet deshalb nicht „Ist es gut?“, sondern „Haben wir diesen Konflikt, und gibt es einen billigeren Weg?“. Diese Seite zeigt mit Messwerten von SQL Server 2025, wann sich der Schalter lohnt, was er kostet und wie Sie das an Ihrer eigenen Last prüfen.

In 60 Sekunden

  • Optimized Locking braucht ADR, und seinen Hauptgewinn (Lock After Qualification) gibt es nur mit RCSI. Wer es einschaltet, schaltet also drei Dinge ein. Die Mechanik steht in Optimized Locking verständlich.
  • Der Gewinn: Schreiber, die über fremde Zeilen laufen, blockieren sich nicht mehr. Im Lab beinahe doppelter Durchsatz in genau diesem Fall, dazu viel weniger gehaltene Sperren und kaum noch Lock Escalation.
  • Die Kosten kommen von ADR: bei Änderungen vieler Zeilen ein Viertel bis zwei Fünftel weniger Durchsatz, das 2,3- bis 3,5-Fache an CPU und gut dreimal so viel Log. Bei Einzelzeilen-Änderungen war kein Unterschied messbar.
  • Ein passender Index war besser: dreimal so viel Durchsatz wie Optimized Locking ohne Index, ganz ohne die ADR-Kosten.
  • Heiße Zeilen: Bei vielen Schreibern auf derselben Zeile blieb der Durchsatz gleich, die langsamsten Schreiber wurden aber doppelt so langsam.
  • Entscheiden Sie per Messung: Messfenster vorher, Schalter einzeln, Messfenster nachher. Die Abfrage dafür steht unten.

Was Sie kaufen und was Sie bezahlen

Wir haben drei Konfigurationen mit vier typischen Lasten verglichen, je 30 Sekunden, drei Wiederholungen, auf SQL Server 2025 CU10 mit acht CPUs. Gemessen wurden erfolgreiche Änderungen je Sekunde:

LastSchreiberohne ADR und RCSIADR + RCSIADR + RCSI + Optimized Locking
Mengen-UPDATE ohne passenden Index (1.000 Zeilen je Änderung)173,652,964,2
869,348,5128,7
dieselbe Änderung mit passendem Schlüssel192,271,075,4
8386,0230,5235,0
Einzelzeile per Primärschlüssel1121,8121,9118,5
8584,1589,7639,4
eine heiße Zeile, 4 Schreiber und 4 Leser4129,3135,2134,2

Daraus lassen sich vier Dinge ablesen:

  • Gewinn nur bei einem Muster. Optimized Locking holt seinen Vorteil dort, wo acht Schreiber ohne Index über alle Zeilen laufen und dabei aufeinander warten. Ohne Optimized Locking warteten sie zusammen rund 205 Sekunden je 30-Sekunden-Lauf auf LCK_M_U, im Mittel also fast sieben Sitzungen gleichzeitig. Mit Optimized Locking wartete niemand mehr auf eine Sperre; der neue Engpass waren Seitenlatches und das Log.
  • Ein einzelner Schreiber zahlt nur. Ohne Konkurrenz gibt es nichts zu entsperren. Es bleiben die ADR-Kosten.
  • ADR kostet bei Mengenänderungen. ADR + RCSI lag dort 23 bis 40 Prozent unter der Konfiguration ohne beides, brauchte je Änderung das 2,3- bis 3,5-Fache an CPU und schrieb gut dreimal so viel Log (bei acht Schreibern mit passendem Schlüssel 412 statt 129 KB je Änderung). Die Ursache: ADR schreibt für jede geänderte Zeile eine Version in die Datenbank, und die muss ins Log.
  • Einzelzeilen-Änderungen merken davon nichts. Bei Änderungen über den Primärschlüssel lagen alle drei Konfigurationen innerhalb weniger Prozent, um eine Millisekunde CPU je Änderung, begrenzt vom Schreiben ins Log.
ADR + RCSI48,5 Änderungen/s207 s, davon 205 s SperrenADR + RCSI + Optimized Locking128,7 Änderungen/s107 s, keine Sperren050100150200 sSperrenSeitenlatchesLog schreibenCPU-WarteschlangeSumme der Wartezeit aller Sitzungen in einem 30-s-Lauf, Median aus drei Läufen. Acht Schreiber ändern perScan ohne passenden Index je ihre eigene Gruppe von Zeilen.
ADR + RCSI207 s, davon 205 s Sperren48,5 Änderungen/sADR + RCSI + Optimized Locking107 s, keine Sperren128,7 Änderungen/s050100150200 sSperrenSeitenlatchesLog schreibenCPU-WarteschlangeSumme der Wartezeit aller Sitzungen in einem30-s-Lauf, Median aus drei Läufen. AchtSchreiber ändern per Scan ohne passenden Indexje ihre eigene Gruppe von Zeilen.
Optimized Locking beseitigt das Warten auf Sperren; der Engpass wandert zu Seitenlatches und Log. Gemessen im PSG-Lab auf SQL Server 2025 CU10, Median aus drei Läufen.

Dazu kommen die Gewinne, die keine Durchsatzzahl zeigt: Ein UPDATE über 1.000 Zeilen hielt in LAB-10 mit ADR und RCSI 1.060 Sperren, mit Optimized Locking 3. Weniger Sperren heißt weniger Lock-Speicher und kaum noch Lock Escalation. Microsoft nennt aus Telemetrie von rund 25.000 Azure-SQL-Datenbanken, dass bei Nonclustered-Indizes etwa 81 Prozent der Sperren übersprungen werden. Das sind Sperrzahlen, keine Zeitwerte; Durchsatz- oder Latenzzahlen veröffentlicht Microsoft nicht, und unabhängige Benchmarks haben wir nicht gefunden.

Profitiert Ihr Workload? Fünf Fragen

  1. Warten Schreiber auf Schreiber, die andere Zeilen ändern? In den Wartezeiten zeigt sich das als LCK_M_U, oft zusammen mit Scans in den Plänen der wartenden UPDATE- und DELETE-Anweisungen. Das ist der Fall, den Optimized Locking löst.
  2. Scannen diese Änderungen, weil ein Index fehlt? Dann prüfen Sie zuerst den Index (nächster Abschnitt).
  3. Steht im Code etwas, das den Gewinn verhindert? Sperrhinweise wie UPDLOCK, HOLDLOCK oder READCOMMITTEDLOCK, andere Isolationsstufen als READ COMMITTED, MERGE, OUTPUT an den Client, Columnstore-Indizes oder Pläne mit mehreren Zugriffen auf die Zieltabelle: Dort greift Lock After Qualification nicht.
  4. Halten große Transaktionen viele Sperren, gibt es Lock Escalation? Dann hilft Optimized Locking auch ohne Wartezeiten, denn es reduziert Lock-Speicher und Eskalationen. Ausnahme: Direkt nach dem Laden oder einem Rebuild mit Füllfaktor 100 eskalierte im Lab auch mit Optimized Locking jeder erste große Lauf, weil sich die Seiten teilen (Details).
  5. Ist RCSI schon an oder fachlich vertretbar? Ohne RCSI bleibt vom Gewinn wenig. Was RCSI an Ihrem Code ändert, steht in NOLOCK, RCSI oder SNAPSHOT?.

Wer bei Frage 1 nichts findet und bei Frage 4 auch nicht, kauft mit Optimized Locking vor allem die ADR-Kosten.

Index oder Optimized Locking?

Im Lab war es dieselbe Tabelle, dieselben Daten und dieselbe Anweisung:

UPDATE dbo.Scan SET v = v + 1 WHERE g = @g;   -- 8.000 Zeilen in acht Gruppen, jede Sitzung ändert ihre eigene Gruppe

Einmal mit dem Clustered Key nur auf id, sodass jede Änderung alle Zeilen lesen muss, einmal mit dem Clustered Key (g, id), sodass jede Änderung nur ihre eigene Gruppe liest.

Änderungen je SekundeAcht Schreiber, Medianohne Schlüssel, ohne ADR und RCSI69,3ohne Schlüssel, mit Optimized Locking128,7passender Schlüssel, ohne ADR und RCSI386,0passender Schlüssel, mit Optimized Locking235,00100200300400CPU je ÄnderungContainer-CPU geteilt durch Änderungenohne Schlüssel, ohne ADR und RCSI8,9 msohne Schlüssel, mit Optimized Locking26,2 mspassender Schlüssel, ohne ADR und RCSI4,3 mspassender Schlüssel, mit Optimized Locking10,5 ms0102030 msOrange: mit ADR und RCSI, die Kosten von ADR fallen an. Log je Änderung ohne ADR 121 bzw. 129 KB, mitOptimized Locking 379 bzw. 378 KB.
Änderungen je SekundeAcht Schreiber, Medianohne Schlüssel, ohne ADR und RCSI69,3ohne Schlüssel, mit Optimized Locking128,7passender Schlüssel, ohne ADR und RCSI386,0passender Schlüssel, mit Optimized Locking235,00100200300400CPU je ÄnderungContainer-CPU geteilt durch Änderungenohne Schlüssel, ohne ADR und RCSI8,9 msohne Schlüssel, mit Optimized Locking26,2 mspassender Schlüssel, ohne ADR und RCSI4,3 mspassender Schlüssel, mit Optimized Locking10,5 ms0102030 msOrange: mit ADR und RCSI, die Kosten von ADRfallen an. Log je Änderung ohne ADR 121 bzw. 129KB, mit Optimized Locking 379 bzw. 378 KB.
Der passende Schlüssel ohne ADR schaffte 3,0-mal so viele Änderungen wie Optimized Locking ohne Index, bei einem Sechstel der CPU je Änderung. Gemessen im PSG-Lab auf SQL Server 2025 CU10, Median aus drei Läufen à 30 s.

Der passende Zugriffspfad beseitigt den Konflikt an der Wurzel: Keine Sitzung liest mehr die Zeilen der anderen, also wartet auch keine. Optimized Locking lässt die Sitzungen nur ohne Warten aneinander vorbeilesen; die Arbeit für das Lesen fremder Zeilen und die ADR-Kosten bleiben.

Daraus folgt die Reihenfolge: Erst den Zugriffspfad, dann den Schalter. Optimized Locking ist die richtige Wahl, wenn Sie den Code oder die Indizes nicht ändern dürfen, etwa bei Herstellersoftware, und der Konflikt „Schreiber über fremde Zeilen“ tatsächlich auftritt.

Die Grenze dieses Vergleichs: Wir haben den Clustered Key geändert. Ein zusätzlicher Nonclustered-Index auf g wurde vom Optimierer bei einem Achtel Selektivität nicht genutzt. Ein realer Zusatzindex kostet Platz, Pflege und manchmal Key Lookups; das haben wir nicht gemessen.

Heiße Zeilen: gleicher Durchsatz, längere Ausreißer

Vier Sitzungen erhöhen denselben Zähler, vier weitere lesen ihn:

95 % der Schreiber schneller alsje Änderung, Median aus drei Läufenohne ADR und RCSI33,1 msADR + RCSI36,7 msADR + RCSI + Optimized Locking70,0 ms03570105140 ms99 % der Schreiber schneller alsje Änderung, Median aus drei Läufenohne ADR und RCSI48,6 msADR + RCSI49,3 msADR + RCSI + Optimized Locking133,0 ms03570105140 msVier Schreiber erhöhen dieselbe Zeile, vier Leser lesen sie. Durchsatz der Schreiber gleich (129,3 /135,2 / 134,2 Änderungen/s), Sperrwartezeit der Schreiber ebenso (87 / 86 / 81 s je Lauf); mit OptimizedLocking warten sie auf die Transaktion (LCK_M_S_XACT_MODIFY).
95 % der Schreiber schneller alsje Änderung, Median aus drei Läufenohne ADR und RCSI33,1 msADR + RCSI36,7 msADR + RCSI + Optimized Locking70,0 ms03570105140 ms99 % der Schreiber schneller alsje Änderung, Median aus drei Läufenohne ADR und RCSI48,6 msADR + RCSI49,3 msADR + RCSI + Optimized Locking133,0 ms03570105140 msVier Schreiber erhöhen dieselbe Zeile, vierLeser lesen sie. Durchsatz der Schreiber gleich(129,3 / 135,2 / 134,2 Änderungen/s),Sperrwartezeit der Schreiber ebenso (87 / 86 /81 s je Lauf); mit Optimized Locking warten sieauf die Transaktion (LCK_M_S_XACT_MODIFY).
Gleicher Durchsatz, aber längere Ausreißer: Mit Optimized Locking brauchten die langsamsten fünf Prozent der Schreiber 1,9-mal so lange wie mit ADR und RCSI allein. Gemessen im PSG-Lab auf SQL Server 2025 CU10, ein synthetischer Fall.

Der Durchsatz der Schreiber bleibt gleich, ihre gesamte Wartezeit auch. Mit Optimized Locking ist sie aber ungleicher verteilt: Die langsamsten fünf Prozent der Schreiber brauchten doppelt so lange, das langsamste Prozent fast dreimal so lange. Für Anwendungen mit Antwortzeit-Zusagen auf solchen Zählern, Sequenztabellen oder Statuszeilen ist das ein Grund, vorher zu messen. Microsoft beschreibt das nicht; in den Quellen fanden wir dazu nur einen Erfahrungsbericht ohne Messwerte.

Unsere Vermutung zur Ursache, nicht geprüft: Mit Optimized Locking wartet ein Schreiber auf die Transaktion des anderen, nicht auf die Zeile. Nach deren Commit muss er die Zeile erneut anfordern, und die geordnete Warteschlange einer klassischen Zeilensperre gibt es dabei nicht.

Die Leser zeigen nebenbei, was RCSI leistet: 47-mal so viele Abfragen, mit und ohne Optimized Locking. Wer Leser entlasten will, braucht RCSI, nicht Optimized Locking.

Vorher messen: das Messrezept

Eine Umstellung beurteilen Sie am besten mit derselben Messung vorher und nachher, zur selben Lastzeit. Die folgende Abfrage misst ein Zeitfenster: Sie merkt sich die Zähler, wartet die eingestellte Zeit und zeigt dann, was in diesem Fenster passiert ist. Wartezeiten auf Sperren erscheinen einschließlich der neuen LCK_M_S_XACT_*-Typen, dazu Log und Versionsspeicher.

Abfrage: Messfenster vorher und nachher

Abfrage 4: Messfenster vorher und nachher · nur lesend · im PSG-Lab geprüft, Versionen im Kopf
/* PSG Stack · SQL Server Locking · Abfrage 4   Messfenster: Was hat sich in einem festen Zeitfenster getan? Wartezeiten auf Sperren (auch die neuen   XACT-Wartetypen), Sperr- und Deadlock-Zähler, geschriebenes Log und Versionsspeicher.   Einmal vor und einmal nach einer Umstellung zur gleichen Lastzeit ausführen und vergleichen.    !!! WARNUNG — bitte vor dem Ausführen lesen !!!   - Nur lesend. Die Abfrage ändert keine Daten und keine Einstellung. Sie legt nur kleine     Tabellenvariablen in der eigenen Sitzung an.   - Sie WARTET absichtlich: Die Sitzung bleibt @Sekunden lang offen (WAITFOR DELAY) und     liefert erst danach Ergebnisse. Ein Abbruch ist jederzeit gefahrlos möglich.   - Gemessen wird der ganze Server (Wartezeiten, Zähler), Log und Versionsspeicher aber nur für     die Datenbank, in der Sie die Abfrage starten. Andere Last im selben Fenster zählt mit.   - Rechte: VIEW SERVER STATE (ab SQL Server 2022 genügt VIEW SERVER PERFORMANCE STATE),     dazu ein Benutzer in der Datenbank, die Sie messen.   - MAXDOP 1: Jede Teilabfrage läuft seriell und belegt keine weiteren Kerne.   - Ab SQL Server 2017. Was eine Version nicht kennt (Versionsspeicher-DMV vor 2019, Optimized     Locking vor 2025), erscheint als n/a. Geprüft im PSG-Lab auf 2017, 2019, 2022 und 2025     (10.11.2026), unter Last auf 2025 CU10, auch ohne sysadmin.    Ergebnisse:     1  Schalter der aktuellen Datenbank (RCSI, ADR, Optimized Locking)     2  Wartezeiten im Fenster, nur Sperren, Log und Seitenlatches, größte zuerst     3  Zähler im Fenster: Sperranforderungen, Sperrwartevorgänge, Timeouts, Deadlocks, Eskalationen     4  Log und Versionsspeicher der aktuellen Datenbank*/SET NOCOUNT ON; DECLARE @Sekunden int = 60;   -- Länge des Messfensters in Sekunden (1 bis 3600) DECLARE @w0 TABLE (wait_type nvarchar(60) PRIMARY KEY, anzahl bigint, ms bigint, signal_ms bigint);DECLARE @z0 TABLE (zaehler nvarchar(128) PRIMARY KEY, wert bigint);DECLARE @w1 TABLE (wait_type nvarchar(60) PRIMARY KEY, anzahl bigint, ms bigint, signal_ms bigint);DECLARE @z1 TABLE (zaehler nvarchar(128) PRIMARY KEY, wert bigint);DECLARE @log0 bigint, @log1 bigint, @pvs0 bigint, @pvs1 bigint, @t0 datetime2(3), @t1 datetime2(3);DECLARE @hatPvs bit = CASE WHEN OBJECT_ID(N'sys.dm_tran_persistent_version_store_stats') IS NULL THEN 0 ELSE 1 END;DECLARE @pvsSql nvarchar(400) = N'SELECT @p = persistent_version_store_size_kb FROM sys.dm_tran_persistent_version_store_stats WHERE database_id = DB_ID() OPTION (MAXDOP 1);';DECLARE @warte char(8); IF @Sekunden NOT BETWEEN 1 AND 3600 BEGIN RAISERROR(N'@Sekunden muss zwischen 1 und 3600 liegen.', 16, 1); RETURN; END;SET @warte = CONVERT(char(8), DATEADD(second, @Sekunden, CONVERT(datetime, 0)), 108); /* Stand vorher */SET @t0 = SYSUTCDATETIME();INSERT @w0SELECT wait_type, waiting_tasks_count, wait_time_ms, signal_wait_time_msFROM sys.dm_os_wait_statsWHERE wait_type LIKE N'LCK[_]M[_]%' OR wait_type IN (N'WRITELOG', N'PAGELATCH_EX', N'PAGELATCH_SH')OPTION (MAXDOP 1);INSERT @z0SELECT RTRIM(counter_name), cntr_valueFROM sys.dm_os_performance_countersWHERE (object_name LIKE N'%:Locks%' AND instance_name = N'_Total'       AND counter_name IN (N'Lock Requests/sec', N'Lock Waits/sec', N'Lock Timeouts (timeout > 0)/sec', N'Number of Deadlocks/sec'))   OR (object_name LIKE N'%:Access Methods%' AND counter_name = N'Table Lock Escalations/sec')OPTION (MAXDOP 1);SELECT @log0 = SUM(v.num_of_bytes_written)FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vJOIN sys.database_files AS f ON f.file_id = v.file_id AND f.type = 1OPTION (MAXDOP 1);IF @hatPvs = 1 EXEC sys.sp_executesql @pvsSql, N'@p bigint OUTPUT', @p = @pvs0 OUTPUT; WAITFOR DELAY @warte; /* Stand nachher */SET @t1 = SYSUTCDATETIME();INSERT @w1SELECT wait_type, waiting_tasks_count, wait_time_ms, signal_wait_time_msFROM sys.dm_os_wait_statsWHERE wait_type LIKE N'LCK[_]M[_]%' OR wait_type IN (N'WRITELOG', N'PAGELATCH_EX', N'PAGELATCH_SH')OPTION (MAXDOP 1);INSERT @z1SELECT RTRIM(counter_name), cntr_valueFROM sys.dm_os_performance_countersWHERE (object_name LIKE N'%:Locks%' AND instance_name = N'_Total'       AND counter_name IN (N'Lock Requests/sec', N'Lock Waits/sec', N'Lock Timeouts (timeout > 0)/sec', N'Number of Deadlocks/sec'))   OR (object_name LIKE N'%:Access Methods%' AND counter_name = N'Table Lock Escalations/sec')OPTION (MAXDOP 1);SELECT @log1 = SUM(v.num_of_bytes_written)FROM sys.dm_io_virtual_file_stats(DB_ID(), NULL) AS vJOIN sys.database_files AS f ON f.file_id = v.file_id AND f.type = 1OPTION (MAXDOP 1);IF @hatPvs = 1 EXEC sys.sp_executesql @pvsSql, N'@p bigint OUTPUT', @p = @pvs1 OUTPUT; /* 1 · Schalter der aktuellen Datenbank */DECLARE @schalter nvarchar(max) = N'SELECT d.name AS datenbank,       d.is_read_committed_snapshot_on AS rcsi,       d.snapshot_isolation_state_desc AS snapshot_isolation,       ' + CASE WHEN COL_LENGTH(N'sys.databases', N'is_accelerated_database_recovery_on') IS NULL                THEN N'N''n/a''' ELSE N'CONVERT(nvarchar(3), d.is_accelerated_database_recovery_on)' END + N' AS adr,       ' + CASE WHEN COL_LENGTH(N'sys.databases', N'is_optimized_locking_on') IS NULL                THEN N'N''n/a''' ELSE N'CONVERT(nvarchar(3), d.is_optimized_locking_on)' END + N' AS optimized_lockingFROM sys.databases AS dWHERE d.database_id = DB_ID()OPTION (MAXDOP 1);';EXEC sys.sp_executesql @schalter; /* 2 · Wartezeiten im Fenster */SELECT w1.wait_type,       CASE WHEN w1.wait_type LIKE N'%XACT%' THEN N'Optimized Locking (XACT)'            WHEN w1.wait_type LIKE N'LCK%' THEN N'Sperre'            WHEN w1.wait_type = N'WRITELOG' THEN N'Log schreiben'            ELSE N'Seitenlatch' END AS art,       w1.anzahl - ISNULL(w0.anzahl, 0) AS anzahl,       w1.ms - ISNULL(w0.ms, 0) AS wartezeit_ms,       CONVERT(decimal(12, 2), (w1.ms - ISNULL(w0.ms, 0)) * 1.0 / NULLIF(w1.anzahl - ISNULL(w0.anzahl, 0), 0)) AS ms_je_warten,       CONVERT(decimal(12, 2), (w1.ms - ISNULL(w0.ms, 0)) / 1000.0 / NULLIF(DATEDIFF(millisecond, @t0, @t1) / 1000.0, 0)) AS wartende_im_mittelFROM @w1 AS w1LEFT JOIN @w0 AS w0 ON w0.wait_type = w1.wait_typeWHERE w1.anzahl > ISNULL(w0.anzahl, 0)ORDER BY wartezeit_ms DESCOPTION (MAXDOP 1); /* 3 · Zähler im Fenster */SELECT z1.zaehler,       z1.wert - ISNULL(z0.wert, 0) AS im_fenster,       CONVERT(decimal(14, 2), (z1.wert - ISNULL(z0.wert, 0)) / NULLIF(DATEDIFF(millisecond, @t0, @t1) / 1000.0, 0)) AS je_sekundeFROM @z1 AS z1LEFT JOIN @z0 AS z0 ON z0.zaehler = z1.zaehlerORDER BY z1.zaehlerOPTION (MAXDOP 1); /* 4 · Log und Versionsspeicher der aktuellen Datenbank */SELECT DB_NAME() AS datenbank,       DATEDIFF(millisecond, @t0, @t1) / 1000.0 AS fenster_s,       CONVERT(decimal(14, 2), (@log1 - @log0) / 1048576.0) AS log_geschrieben_mb,       CASE WHEN @hatPvs = 0 THEN N'n/a' ELSE CONVERT(nvarchar(20), @pvs0) END AS pvs_vorher_kb,       CASE WHEN @hatPvs = 0 THEN N'n/a' ELSE CONVERT(nvarchar(20), @pvs1) END AS pvs_nachher_kbOPTION (MAXDOP 1);

Beispielausgabe

Ein 20-Sekunden-Fenster aus dem Lab (SQL Server 2025 CU10, acht Schreiber ohne passenden Index, ADR, RCSI und Optimized Locking an, synthetische Daten, Spalten gekürzt):

Beispielausgabe
1 · Schalter
datenbank  rcsi  snapshot_isolation  adr  optimized_locking
---------  ----  ------------------  ---  -----------------
ol_full    1     OFF                 1    1

2 · Wartezeiten im Fenster
wait_type     art            anzahl  wartezeit_ms  ms_je_warten  wartende_im_mittel
------------  -------------  ------  ------------  ------------  ------------------
PAGELATCH_SH  Seitenlatch     42762         32691          0.76                1.63
PAGELATCH_EX  Seitenlatch     20838         18586          0.89                0.92
WRITELOG      Log schreiben    2869          9595          3.34                0.48

3 · Zähler im Fenster
zaehler                          im_fenster  je_sekunde
-------------------------------  ----------  ----------
Lock Requests/sec                   9459593   470696.77
Lock Timeouts (timeout > 0)/sec           0        0.00
Lock Waits/sec                            0        0.00
Number of Deadlocks/sec                   0        0.00
Table Lock Escalations/sec                0        0.00

4 · Log und Versionsspeicher
datenbank  fenster_s  log_geschrieben_mb  pvs_vorher_kb  pvs_nachher_kb
---------  ---------  ------------------  -------------  --------------
ol_full    20.097000             1033.42         114056          572808

So lesen Sie das:

  • Ergebnis 2: Keine einzige Sperrwartezeit, dafür Seitenlatches und Log. In unseren Läufen derselben Last ohne Optimized Locking warteten an dieser Stelle im Mittel fast sieben Sitzungen auf LCK_M_U. Der Engpass ist gewandert, das ist der Gewinn. Erscheinen nach dem Einschalten neue LCK_M_S_XACT_*-Zeilen, warten Sitzungen auf andere Transaktionen, etwa auf heißen Zeilen.
  • Ergebnis 3: Lock Waits und Table Lock Escalations sollten nach der Umstellung sinken. Die Zahl der Sperranforderungen sinkt nicht unbedingt, denn kurze Zeilensperren gibt es weiterhin.
  • Ergebnis 4: Das Log und der Versionsspeicher zeigen die ADR-Kosten. Steigt das Log je Fenster deutlich, prüfen Sie Platz, Sicherung und Replikation. Der Versionsspeicher darf schwanken, solange er wieder schrumpft; wächst er von Fenster zu Fenster, hält etwas die Bereinigung auf.

Ergänzend gehören in jede Vorher-nachher-Messung die Antwortzeiten der wichtigsten Vorgänge aus Sicht der Anwendung, auch die langsamsten fünf Prozent, und die CPU der Instanz. Das Extended Event locking_stats meldet etwa alle fünf Minuten je Datenbank Eskalationen und wie oft Lock After Qualification versucht wurde; es zeigt also auch, ob der Gewinn überhaupt greift.

Der PoC in fünf Schritten

  1. Testsystem mit echter Last. Ein Replay oder ein Lasttest mit den Vorgängen, die heute warten. Auf einem leeren System sehen Sie nur die Kosten. SQL Server 2025 mindestens mit CU9: Microsoft hat Abstürze von Optimized Locking im Zusammenspiel mit Change Tracking (CU3) und mit weitergeleiteten Zeilen in Heaps (CU9) behoben.
  2. Messfenster ohne alles. Abfrage 4 zu den Lastzeiten, dazu Antwortzeiten und CPU.
  3. Schalter einzeln. Erst ADR, messen. Dann RCSI, messen. Dann Optimized Locking, messen. Nur so wissen Sie, welcher Schritt was kostet und was bringt. In unserem Lab kostete ADR + RCSI bis zu 40 Prozent, und erst Optimized Locking holte es im passenden Fall mehr als zurück.
  4. Ergebnisse prüfen, nicht nur Zeiten. RCSI ändert, was Trigger, Prüflogik und Lesen-dann-Schreiben-Code sehen; Optimized Locking ändert in einem Grenzfall die Reihenfolge von Änderungen. Beides steht in NOLOCK, RCSI oder SNAPSHOT? und Optimized Locking verständlich. Testen Sie diese Stellen mit zwei Sitzungen.
  5. Monitoring und Deadlock-Auswertung prüfen. Erkennt Ihr Monitoring die Wartetypen LCK_M_S_XACT_READ, LCK_M_S_XACT_MODIFY und LCK_M_S_XACT? Liest Ihre Deadlock-Auswertung das Element <xactlock>? Wenn nicht, sieht Blocking nach dem Einschalten so aus, als wäre es verschwunden.

Woran Sie eine Regression erkennen

AnzeichenWahrscheinliche UrsacheWas Sie tun
Durchsatz sinkt, CPU und Log steigen bei MengenänderungenADR-Kosten ohne passenden KonfliktMessung aus Schritt 3 ansehen; ohne Gewinn zurücknehmen
Versionsspeicher wächst von Fenster zu FensterLange Transaktion, lange Abfrage oder vergessene offene Transaktion hält die Bereinigung aufHalter in sys.dm_tran_persistent_version_store_stats (oldest_active_transaction_id) suchen; Version Store und Transaktionslog
Die langsamsten Schreiber werden langsamer, Durchsatz gleichHeiße Zeilen, neue LCK_M_S_XACT_MODIFY-WartezeitenHotspot entschärfen (Verteilung, Sequenzen) oder Optimized Locking für diese Datenbank zurücknehmen
Lock Escalation trotz Optimized LockingErste Änderung frisch geladener Zeilen auf vollen SeitenNach großen Ladevorgängen erwartbar; Füllfaktor prüfen (Details)
lock_after_qual_stmt_abort häufig, Laufzeit einzelner Änderungen schwanktLock After Qualification startet Anweisungen neuDie Heuristik schaltet LAQ bei zu viel verschwendeter Arbeit selbst ab, mit Query Store je Plan, sonst für die ganze Datenbank; Query Store eingeschaltet lassen
Blocking-Dashboard bleibt leer, Anwender klagenMonitoring kennt die XACT-Wartetypen nichtFilter um LCK_M_S_XACT* erweitern

Umschalten in der Produktion

Alle drei Schalter brauchen einen Moment, in dem keine andere Verbindung in der Datenbank aktiv ist:

ALTER DATABASE [MeineDB] SET ACCELERATED_DATABASE_RECOVERY = ON WITH ROLLBACK IMMEDIATE;ALTER DATABASE [MeineDB] SET READ_COMMITTED_SNAPSHOT = ON WITH ROLLBACK IMMEDIATE;ALTER DATABASE [MeineDB] SET OPTIMIZED_LOCKING = ON WITH ROLLBACK IMMEDIATE;

Was wir im Lab auf SQL Server 2017 bis 2025 gesehen haben:

  • Ohne Zusatz wartet die Anweisung, und zwar auf jede andere Verbindung in der Datenbank, auch eine untätige ohne Transaktion. Hatte die andere Sitzung eine Transaktion, wartete ALTER DATABASE über deren Commit hinaus, bis die Verbindung getrennt war.
  • WITH NO_WAIT half nicht. Laut Dokumentation soll die Anweisung damit sofort scheitern. Im Lab wartete sie auf allen Versionen und bei allen drei Schaltern genauso lange wie ohne Zusatz, ohne Meldung. Die Ursache haben wir nicht geklärt.
  • WITH ROLLBACK IMMEDIATE trennt die anderen Verbindungen und rollt offene Transaktionen zurück. Im Lab war der Schalter nach rund drei Sekunden umgelegt.

Planen Sie also ein Wartungsfenster, in dem Abbrüche akzeptabel sind. Zurück geht es in umgekehrter Reihenfolge mit OFF: erst Optimized Locking, dann RCSI nach Bedarf, dann ADR. Andersherum lehnt SQL Server ab (Msg 12134, siehe Optimized Locking verständlich). Optimized Locking gibt es in SQL Server 2025 Enterprise und Standard, nicht in Express; in Azure SQL Database ist es immer an.

Was Ihre Werkzeuge noch nicht kennen

Optimized Locking verändert, was Diagnosewerkzeuge zu sehen bekommen: neue Wartetypen, Sperren auf XACT statt auf Zeilen, ein neues Element im Deadlock-Report. Manche Community-Werkzeuge kennen das schon, andere noch nicht, und der Stand ändert sich mit jeder Version. Prüfen Sie deshalb Ihr eigenes Monitoring, bevor Sie umstellen: Zeigt es eine Sitzung, die auf LCK_M_S_XACT_MODIFY wartet, als blockiert an? Löst es den Blocker auf? Wertet es einen Deadlock mit <xactlock> aus? Am einfachsten testen Sie das mit dem Lagebild aus der Übersicht und zwei Sitzungen auf einem Testsystem.

Methode und Grenzen

Die Workload-Messung lief am 10.11.2026 auf SQL Server 2025 CU10 (17.0.5035.5), Developer Edition im Linux-Container mit acht CPUs, 12 GB Arbeitsspeicher für SQL Server und MAXDOP 1, Daten und Log auf einer gemeinsamen SATA-SSD. 63 Läufe zu je 30 Sekunden, drei Wiederholungen je Punkt in zufälliger Reihenfolge, Mediane; alle Läufe ohne Fehler, Deadlocks oder Tabelleneskalationen, und nach jedem Lauf stimmten die Summen mit den erfolgreichen Änderungen überein. Acht Läufe überschnitten sich mit einem anderen Versuch auf demselben Host; allein wiederholt lagen sie 3 Prozent darunter bis 14 Prozent darüber, an den Aussagen änderte das nichts. Das Umschalten haben wir in einem eigenen Versuch auf SQL Server 2017 CU31, 2019 CU32, 2022 CU27 und 2025 CU10 gemessen. Grenzen:

  • Synthetische Lasten, eine geschlossene Lastschleife ohne Ankunftsrate, ein Host. Das Log war in mehreren Fällen der Engpass und begrenzt alle Konfigurationen gleich; auf schnellerem Speicher können die Abstände anders ausfallen.
  • Keine Langzeitmessung: Wie sich Versionsspeicher und Bereinigung über Stunden mit einer langen Lesetransaktion verhalten, haben wir nicht gemessen.
  • Die Ursache der längeren Ausreißer auf heißen Zeilen ist eine Vermutung.
  • Den Indexvergleich haben wir mit einem anderen Clustered Key gemacht; die Kosten eines realen Zusatzindex sind nicht gemessen.
  • Azure SQL Database, Managed Instance und Windows haben wir nicht gemessen.
  • Prozentwerte gelten für diese Lasten. Sie sind keine Prognose für Ihr System.

Weiterlesen

Sie planen das Upgrade auf SQL Server 2025?

Dann stellt sich die Frage nach Optimized Locking ohnehin. In einer Performance-Analyse messen wir an Ihrer Last, ob der Konflikt auftritt, den Optimized Locking löst, ob ein Index ihn billiger beseitigt und was ADR bei Ihnen kostet. Den Gesamtblick auf die Instanz gibt der SQL Server Health Check.

Weitere Locking-Themen