sql >> Database >  >> RDS >> Database

De 2008 R2-bugfix die RCSI doorbreekt

Een van de correcties in de cumulatieve update 11 voor SQL Server 2008 R2 Service Pack 2 lost een 'onjuiste impasse' op die in een specifiek scenario kan optreden (later in dit artikel uitgelegd). Helaas introduceert de fix een nieuwe bug, waarbij SELECT-query's onder RCSI (lees Committed snapshot isolation) beginnen met het nemen van intent-shared locks op tabelniveau. Als gevolg hiervan kunt u na toepassing van 2008 R2 SP2 CU11 (of later) meer blokkering (en mogelijk deadlocking) zien voor RCSI-query's.

Dit zal als een onwelkome verrassing komen voor iedereen die gewend is aan lezers die schrijvers niet blokkeren (en vice versa) bij het gebruik van RCSI. Er is op het moment van schrijven geen oplossing voor de RCSI-bug. Het Connect-item dat door Eugene Karpovich is gemaakt om het probleem te melden, is zelfs gesloten als "zal niet worden opgelost", hoewel ik begrijp dat deze beslissing momenteel wordt beoordeeld.

Normaal gesproken is dit probleem misschien niet zo'n groot probleem, omdat cumulatieve updates over het algemeen niet zo wijdverbreid worden toegepast als volledige servicepacks. Microsoft heeft echter onlangs aangekondigd dat er een Final Service Pack 3 voor SQL Server 2008 R2 komt. Dit servicepack is een eenvoudige roll-up van bestaande cumulatieve SP2-updates (tot en met CU13), maar zonder nieuwe fixes. Het resultaat van dit alles is dat, tenzij er in de tussentijd iets verandert, gebruikers die SP3 toepassen, plotseling worden getroffen door de RCSI-bug die in CU11 is geïntroduceerd.

edit:net voordat dit artikel werd gepubliceerd, bevestigde Microsoft dat deze regressie in SP3 zal worden verholpen.

Dezelfde "onjuiste impasse"-bug (waarvan de fix de nieuwe bug introduceert) is ook opgelost in Cumulatieve Update 8 voor SQL Server 2012 Service Pack 1, zoals beschreven in KB2923460. De oplossing voor SQL Server 2012 is anders en doet niet introduceer het nieuwe RCSI-probleem.

SQL Server 2014 is voor zover ik weet nooit door beide problemen getroffen. Er is zeker geen documentatie om iets anders aan te geven, en de tests die ik heb uitgevoerd op 2014 RTM, CU1 en CU2 reproduceren geen van beide bugs.

De RCSI-bug uit 2008 R2

Een SELECT-query die onder RCSI wordt uitgevoerd, heeft doorgaans alleen een Schema Stabiliteit (Sch-S) vergrendeling nodig, die compatibel is met alle andere vergrendelingen, behalve een Schema Modificatie (Sch-M) vergrendeling. Wanneer CU11 (of hoger) wordt toegepast op een SQL Server 2008 R2-exemplaar, nemen deze query's een intent-shared (Tab-IS) vergrendeling op tabelniveau. Het volgende testscript kan worden gebruikt om het verschil in gedrag aan te tonen:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET NOCOUNT ON;
GO
CREATE DATABASE RCSI;
GO
ALTER DATABASE RCSI
SET READ_COMMITTED_SNAPSHOT ON;
GO
ALTER DATABASE RCSI
SET ALLOW_SNAPSHOT_ISOLATION OFF;
GO
USE RCSI;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1), (2), (3), (4);
GO
-- Show locks
DBCC TRACEON (1200, 3604, -1) WITH NO_INFOMSGS;
SELECT * FROM dbo.Test;
DBCC TRACEOFF (1200, 3604, -1) WITH NO_INFOMSGS;
GO
ALTER DATABASE RCSI
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE RCSI;

Wanneer uitgevoerd tegen een exemplaar van SQL Server 2008 R2 zonder de bug, toont de debug-uitvoer een enkele Sch-S-vergrendeling die is genomen voor de testinstructie zoals verwacht:

Proces voor het verkrijgen van Sch-S-vergrendeling op OBJECT:7:2105058535:0 resultaat:OK
Vergrendeling voor procesontgrendeling op OBJECT:7:2105058535:0

Wanneer uitgevoerd tegen SQL Server 2008 R2 build 10.50.4302 (of hoger), is de uitvoer vergelijkbaar met:

Verkrijgen van IS-vergrendeling op OBJECT:7:2105058535:0 resultaat:OK
Vergrendeling van proces op OBJECT:7:2105058535:0

Merk op dat het Sch-S-slot is vervangen door een Tab-IS-slot.

Implicaties en mitigaties

Een intent-shared (IS) slot is nog steeds een zeer compatibel slot, maar het is niet zo concurrency-vriendelijk als Sch-S. De slotcompatibiliteitsmatrix laat zien dat een IS-slot in strijd is met:

  • Sch-M (schemawijziging) – volgens Sch-S
  • BU (bulkupdate)
  • X (exclusief)

De incompatibiliteit met exclusieve (X) vergrendelingen betekent dat lezen onder RCSI wordt geblokkeerd als een gelijktijdig proces een exclusieve vergrendeling op dezelfde bron bevat. Evenzo zal een schrijver die een exclusieve vergrendeling nodig heeft, blokkeren als een gelijktijdige RCSI-lezer een IS-vergrendeling heeft. Excusieve vergrendelingen worden verkregen wanneer gegevens worden gewijzigd en worden vastgehouden tot het einde van de transactie, dus het effect van de bug is dat lezers onder RCSI worden geblokkeerd door gelijktijdige schrijvers (en vice versa) wanneer ze dat niet waren voordat CU11 werd toegepast.

Een belangrijke verzachtende factor is dat de bug alleen een table-level . veroorzaakt intentie-gedeeld slot te nemen. Een gelijktijdige schrijver die een tabelniveau . nodig heeft exclusieve vergrendeling veroorzaakt blokkering (en mogelijk een impasse). Gelijktijdige schrijvers die alleen exclusieve vergrendelingen op een lager niveau (bijv. rij, pagina of partitie) vereisen, zullen echter niet blokkering of een impasse veroorzaken. Op tafelniveau krijgen deze schrijvers alleen een intent-exclusive (IX) slot, dat compatibel is met Tab-IS. De exclusieve vergrendelingen die op lagere niveaus van granulariteit worden genomen, zullen geen conflict veroorzaken.

In de meeste systemen zullen exclusieve vergrendelingen op tafelniveau (Tab-X) relatief ongebruikelijk zijn. Tenzij expliciet gevraagd met behulp van een TABLOCKX-hint, zijn enkele mogelijke oorzaken van een Tab-X-vergrendeling:

  • Vergrendel escalatie van een lagere granulariteit
  • SERIALIZABLE gebruiken zonder een ondersteunende index voor sleutelbereiksloten

Een technische oplossing is om de (redundante) tabelhint toe te voegen WITH (READCOMMITTED) naar elke tabel in elke query die onder RCSI wordt uitgevoerd. Dit gebeurt om de bug te omzeilen, dus er wordt alleen een Sch-S-slot genomen, maar het is nauwelijks een praktisch voorstel.

Ondanks deze oplossingen is het nog steeds onjuist om Tab-IS te gebruiken voor een alleen-lezen-query onder RCSI. Ik hoop dat het kan worden opgelost voor SQL Server 2008 R2 voordat Service Pack 3 wordt uitgebracht.

De "Onjuiste impasse"-bug

Zoals eerder vermeld, wordt de RCSI-bug geïntroduceerd als een neveneffect van een oplossing voor een "onjuiste deadlock"-bug. Dit eerdere probleem is gedocumenteerd voor SQL Server 2008 R2 in KB2929464 en voor SQL Server 2012 in KB2923460. Geen van beide documenten is een toonbeeld van duidelijkheid (of nauwkeurigheid), maar de onderliggende kwestie is best interessant, dus ik wil hier wat tijd aan besteden.

In wezen treedt de impasse op wanneer:

  • Drie of meer gelijktijdige transacties gelezen uit dezelfde tabel
  • De UPDLOCK- en TABLOCK-hints worden in alle drie de gevallen gebruikt
  • De database-instelling READ_COMMITTED_SNAPSHOT is AAN

Merk op dat het niet uitmaakt onder welk isolatieniveau de transacties worden uitgevoerd. Voer eerst het onderstaande installatiescript uit om de bug te reproduceren:

USE master;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
CREATE DATABASE IncorrectDeadlock;
GO
ALTER DATABASE IncorrectDeadlock 
SET READ_COMMITTED_SNAPSHOT ON;
GO
USE IncorrectDeadlock;
GO
CREATE TABLE dbo.Test
(
    id integer IDENTITY NOT NULL, 
    col1 integer NOT NULL,
 
    CONSTRAINT PK_Test
    PRIMARY KEY CLUSTERED (id)
);
GO
INSERT dbo.Test 
    (col1) 
VALUES 
    (1);

Voer vervolgens het volgende script uit in drie afzonderlijke verbindingen (merk op dat de transactie open blijft):

USE IncorrectDeadlock;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
BEGIN TRANSACTION;
SELECT
    T.id,
    T.col1
FROM dbo.Test AS T
    WITH (UPDLOCK, TABLOCK);

Op dit punt heeft de eerste sessie een resultatenset geretourneerd en de andere twee worden geblokkeerd. De "onjuiste impasse" doet zich voor wanneer de eerste sessie de transactie voltooit (hetzij door te voeren of terug te draaien). Wanneer dit gebeurt, meldt een van de andere twee sessies een impasse:

De impasse treedt op omdat de twee eerder geblokkeerde sessies Tab-IX bevatten (exclusief op tafelniveau) en beide hun vergrendeling willen converteren naar Tab-X (exclusief op tafelniveau). Tab-IX is compatibel met een andere Tab-IX, maar niet met Tab-X. Dit is een conversie-impasse (en de ironie hier is dat UPDLOCK vaak wordt gebruikt om conversie-impasses te voorkomen).

Voel je vrij om het transactie-isolatieniveau voor de drie vragen naar wens te variëren. De deadlock zal hoe dan ook optreden, zolang RCSI is ingeschakeld, met dezelfde vergrendelingen. Wanneer de tests zijn voltooid, verwijdert u de testdatabase:

USE IncorrectDeadlock;
 
ALTER DATABASE IncorrectDeadlock
SET SINGLE_USER 
WITH ROLLBACK IMMEDIATE;
 
USE master;
 
DROP DATABASE IncorrectDeadlock;

Analyse en uitleg

Ik kan me persoonlijk niet herinneren dat ik UPDLOCK en TABLOCK ooit samen in mijn code heb gebruikt. Voor mij lijkt deze combinatie van hints intuïtief vreemd omdat SQL Server geen updatevergrendeling op tabelniveau heeft . Dus, wat betekent het zelfs betekent om UPDLOCK- en TABLOCK-hints samen op te geven?

De documentatie zegt dit:

UPDLOCK

Geeft aan dat updatevergrendelingen moeten worden genomen en vastgehouden totdat de transactie is voltooid. UPDLOCK neemt alleen updatevergrendelingen voor leesbewerkingen op rij- of paginaniveau. Als UPDLOCK wordt gecombineerd met TABLOCK, of een vergrendeling op tafelniveau om een ​​andere reden wordt genomen, wordt in plaats daarvan een exclusieve (X) vergrendeling genomen.

Dit suggereert dat de hintcombinatie zou moeten resulteren in een enkele exclusieve tafelvergrendeling. In feite is dit niet helemaal het hele verhaal:

In SQL Server 2000 resulteert het combineren van UPDLOCK- en TABLOCK-hints in Tab-S (een gedeelde tabelvergrendeling) gevolgd door conversie naar Tab-X (exclusieve tabelvergrendeling) onder alle isolatieniveaus behalve READ UNCOMMITTED. Deze reeks vergrendelingen kan resulteren in een impasse waarbij drie of meer sessies betrokken zijn:twee sessies verwerven Tab-S en beide wachten op de andere om te converteren naar Tab-X. Onder READ UNCOMMITTED gebruikt SQL Server 2000 Sch-S en vervolgens Tab-X, wat niet vatbaar is voor deadlock (gewoon normale blokkering).

In SQL Server 2005 en later (zonder de bugfix) zijn de genomen vergrendelingen alleen of RCSI is ingeschakeld of niet. Als RCSI is ingeschakeld, alle isolatieniveaus neem Tab-IX en converteer vervolgens naar Tab-X. Deze volgorde zorgt ervoor dat de bugfix-adressen vastlopen.

Als RCSI niet is ingeschakeld, gedragen de overeenkomende isolatieniveaus zich zoals ze deden onder SQL Server 2000 (Tab-S nemen en vervolgens converteren naar Tab-X). Het (nieuw voor 2005) snapshot-isolatieniveau neemt Sch-S gevolgd door Tab-X. Als gevolg hiervan zijn SI en READ UNCOMMITTED de enige isolatieniveaus die niet vatbaar zijn voor deze impasse in het UPDLOCK, TABLOCK-scenario wanneer RCSI niet is ingeschakeld.

De Deadlock-oplossing

De fix verandert de vergrendelingen die worden genomen wanneer UPDLOCK en TABLOCK samen worden opgegeven, voor alle isolatieniveaus , en ongeacht of RCSI is ingeschakeld of niet. Nadat de fix is ​​toegepast, zorgen UPDLOCK en TABLOCK ervoor dat de engine Tab-SIX verwerft (op tafelniveau gedeeld met exclusieve intentie), die vervolgens wordt geconverteerd naar Tab-X.

Dit voorkomt het impassescenario omdat Tab-SIX niet compatibel is met een andere Tab-SIX. Onthoud dat de impasse plaatsvond toen twee processen Tab-IX vasthielden om te converteren naar Tab-X. Nu Tab-IX is vervangen door Tab-SIX, is het niet mogelijk voor beide om Tab-SIX tegelijkertijd vast te houden. Het resultaat is een normaal blokkeerscenario in plaats van een impasse.

Laatste gedachten

De "onjuiste deadlock"-oplossing lost een bepaald deadlock-scenario op, maar het resulteert nog steeds niet in het gedrag dat ik me voorstel voor de mensen die UPDLOCK en TABLOCK specificeren. Als SQL Server een Tab-U-vergrendeling (update op tabelniveau) had, zou dit gelijktijdige wijzigingen aan de tabel voorkomen, maar gelijktijdige lezers toestaan. Dit is wat ik denk dat de bedoeling zou zijn van mensen die deze hints samen gebruiken, en ik kan zien hoe het nuttig zou kunnen zijn.

De huidige implementatie (waarbij Tab-X uiteindelijk wordt genomen in plaats van de ontbrekende Tab-U) komt niet overeen met deze verwachting omdat Tab-X gelijktijdig lezen voorkomt (tenzij een isolatieniveau voor rijversies wordt gebruikt). In veel gevallen kunnen we net zo goed TABLOCKX specificeren. Het feit dat de fix ook een nieuwe bug introduceert (alleen voor gebruikers van SQL Server 2008 R2) is ook jammer, vooral als de bug later wordt opgenomen in 2008 R2 SP3.

Houd er rekening mee dat de deadlock-fix niet beschikbaar wordt gemaakt voor SQL Server-versies vóór 2008 R2. Deze versies behouden het complexe vergrendelingsgedrag voor UPDLOCK en TABLOCK zoals hierboven beschreven.

Mijn dank gaat uit naar Eugene Karpovich die dit probleem voor het eerst onder mijn aandacht bracht in een commentaar op mijn artikel over gegevenswijzigingen onder RCSI.


  1. Hoe krijg ik informatie over een index- en tabeleigenaar in Oracle?

  2. Hoe installeer ik Azure Data Studio op een Mac

  3. SQLite INTERSECT-operator

  4. Postgresql dwingt een unieke tweerichtingscombinatie van kolommen af