sql >> Database teknologi >  >> RDS >> Database

2008 R2-fejlrettelsen, der bryder RCSI

En af rettelserne inkluderet i kumulativ opdatering 11 til SQL Server 2008 R2 Service Pack 2 adresserer en "forkert deadlock", der kan opstå i et specifikt scenarie (forklaret senere i denne artikel). Desværre introducerer rettelsen en ny fejl, hvor SELECT-forespørgsler under RCSI (læs committed snapshot isolation) begynder at tage hensigtsdelte låse på tabelniveau. Som en konsekvens heraf kan du se øget blokering (og potentielt dødlås) for RCSI-forespørgsler efter anvendelse af 2008 R2 SP2 CU11 (eller nyere).

Dette vil komme som en uvelkommen overraskelse for alle, der er vant til, at læsere ikke blokerer forfattere (og omvendt), når de bruger RCSI. Der er ingen rettelse til RCSI-fejlen i skrivende stund. Faktisk er Connect-elementet oprettet af Eugene Karpovich for at rapportere problemet blevet lukket som "Vil ikke løse", selvom jeg forstår, at denne beslutning i øjeblikket er under revision.

Normalt er dette problem måske ikke så stor bekymring, fordi kumulative opdateringer generelt ikke er så udbredt som fuld service packs. Microsoft annoncerede dog for nylig, at der vil være en Final Service Pack 3 til SQL Server 2008 R2. Denne service pack vil være en simpel oprulning af eksisterende SP2 kumulative opdateringer (op til og inklusive CU13), men uden nye rettelser. Resultatet af alt dette er, at medmindre noget ændrer sig i mellemtiden, vil brugere, der anvender SP3, pludselig begynde at blive påvirket af RCSI-fejlen introduceret i CU11.

rediger:Lige før denne artikel blev publiceret, bekræftede Microsoft, at denne regression vil blive rettet i SP3.

Den samme "forkerte deadlock"-fejl (hvis rettelse introducerer den nye fejl) blev også rettet i Kumulativ opdatering 8 til SQL Server 2012 Service Pack 1 som beskrevet i KB2923460. Rettelsen til SQL Server 2012 er anderledes, og den gør ikke introducere det nye RCSI-problem.

SQL Server 2014 var aldrig påvirket af nogen af ​​problemerne, så vidt jeg kan se. Der er bestemt ingen dokumentation, der indikerer andet, og de test, jeg har udført på 2014 RTM, CU1 og CU2, gengiver ikke nogen af ​​fejlene.

2008 R2 RCSI-fejlen

En SELECT-forespørgsel, der kører under RCSI, tager typisk kun en skemastabilitetslås (Sch-S), som er kompatibel med alle andre låse undtagen en skemamodifikationslås (Sch-M). Når CU11 (eller nyere) anvendes på en SQL Server 2008 R2-instans, begynder disse forespørgsler at tage en hensigtsdelt (Tab-IS) lås på tabelniveau. Følgende testscript kan bruges til at demonstrere forskellen i adfærd:

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;

Når den køres mod en forekomst af SQL Server 2008 R2 uden fejlen, viser fejlfindingsoutputtet en enkelt Sch-S-lås, der er taget til testsætningen som forventet:

Proces anskaffelse af Sch-S-lås på OBJECT:7:2105058535:0 resultat:OK
Procesfrigivelse af lås på OBJECT:7:2105058535:0

Når den køres mod SQL Server 2008 R2 build 10.50.4302 (eller nyere), ligner outputtet:

Procesindhentning af IS-lås på OBJECT:7:2105058535:0 resultat:OK
Procesfrigivelse af lås på OBJECT:7:2105058535:0

Bemærk, at Sch-S-låsen er blevet erstattet af en Tab-IS-lås.

Konsekvenser og begrænsninger

En hensigtsdelt (IS) lås er stadig en meget kompatibel lås, men den er ikke helt så samtidighedsvenlig som Sch-S. Låsekompatibilitetsmatrixen viser, at en IS-lås er i konflikt med:

  • Sch-M (skemaændring) – i henhold til Sch-S
  • BU (masseopdatering)
  • X (eksklusiv)

Inkompatibiliteten med eksklusive (X) låse betyder, at en læsning under RCSI vil blokere, hvis en samtidig proces har en eksklusiv lås på den samme ressource. Ligeledes vil en forfatter, der har brug for en eksklusiv lås, blokere, hvis en samtidig RCSI-læser har en IS-lås. Undskyldende låse opnås, når data ændres og holdes til slutningen af ​​transaktionen, så effekten af ​​fejlen er, at læsere under RCSI vil blive blokeret af samtidige forfattere (og omvendt), når de ikke var før CU11 blev anvendt.

En væsentlig formildende faktor er, at fejlen kun forårsager et tabel-niveau hensigtsdelt lås, der skal tages. En samtidig forfatter, der har brug for et bordniveau eksklusiv lås vil forårsage blokering (og potentielt dødlås). Men samtidige forfattere, der kun kræver eksklusive låse på et lavere niveau (f.eks. række, side eller partition), vil ikke forårsage blokering eller dødvande. På bordniveau vil disse forfattere kun erhverve en hensigts-eksklusiv (IX) lås, som er kompatibel med Tab-IS. De eksklusive låse, der tages ved lavere granularitetsniveauer, vil ikke forårsage en konflikt.

I de fleste systemer vil eksklusive (Tab-X) låse på bordniveau være relativt usædvanlige. Medmindre det udtrykkeligt anmodes om ved hjælp af et TABLOCKX-tip, er nogle mulige årsager til en Tab-X-lås:

  • Lås eskalering fra en lavere granularitet
  • Brug af SERIALIZABLE uden et understøttende indeks for nøglerækkelåse

En teknisk løsning er at tilføje det (redundante) tabeltip WITH (READCOMMITTED) til hver tabel i hver forespørgsel, der kører under RCSI. Dette tilfældigvis omgår fejlen, så kun en Sch-S-lås tages, men det er næppe et praktisk forslag.

På trods af disse begrænsninger er det stadig forkert opførsel at tage Tab-IS til en skrivebeskyttet forespørgsel under RCSI. Jeg håber, det kan rettes til SQL Server 2008 R2, før Service Pack 3 frigives.

Bugen "Forkert dødvande"

Som tidligere nævnt introduceres RCSI-fejlen som en bivirkning af en rettelse af en "forkert deadlock"-fejl. Dette tidligere problem er dokumenteret for SQL Server 2008 R2 i KB2929464 og for SQL Server 2012 i KB2923460. Ingen af ​​dokumenterne er en model for klarhed (eller nøjagtighed), men det underliggende problem er ret interessant, så jeg vil bruge lidt tid på at se på det her.

I bund og grund opstår dødvandet, når:

  • Tre eller flere samtidige transaktioner læst fra samme tabel
  • UPDLOCK- og TABLOCK-tip bruges i alle tre tilfælde
  • Databaseindstillingen READ_COMMITTED_SNAPSHOT er TIL

Bemærk, at det ikke er lige meget, hvilket isolationsniveau transaktionerne kører under. For at reproducere fejlen skal du først køre opsætningsscriptet nedenfor:

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);

Kør derefter følgende script i tre separate forbindelser (bemærk, at transaktionen efterlades åben):

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);

På dette tidspunkt vil den første session have returneret et resultatsæt, og de to andre vil blive blokeret. Den "forkerte deadlock" opstår, når den første session fuldfører sin transaktion (enten ved at binde eller rulle tilbage). Når dette sker, vil en af ​​de to andre sessioner rapportere et dødvande:

Dødlåsen opstår, fordi de to tidligere blokerede sessioner holder Tab-IX (eksklusiv for hensigt på tabelniveau), og begge ønsker at konvertere deres lås til Tab-X (eksklusiv på bordniveau). Tab-IX er kompatibel med en anden Tab-IX, men ikke Tab-X. Dette er en dødvande for konvertering (og det ironiske her er, at UPDLOCK ofte bruges til at undgå dødvande for konverteringer).

Du er velkommen til at variere transaktionsisolationsniveauet for de tre forespørgsler, som du ønsker. Deadlock vil opstå uanset, så længe RCSI er aktiveret, med de samme låse involveret. Når testene er færdige, skal du fjerne testdatabasen:

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

Analyse og forklaring

Jeg kan personligt ikke huske nogensinde at have brugt UPDLOCK og TABLOCK sammen i min kode. For mig virker denne kombination af tip intuitivt mærkelig, fordi SQL Server ikke har en opdateringslås på tabelniveau . Så hvad betyder det overhovedet betyder for at angive UPDLOCK og TABLOCK hints sammen?

Dokumentationen siger dette:

UPDLOCK

Angiver, at opdateringslåse skal tages og holdes, indtil transaktionen er fuldført. UPDLOCK tager kun opdateringslåse til læseoperationer på række- eller sideniveau. Hvis UPDLOCK kombineres med TABLOCK, eller der tages en lås på bordniveau af en anden årsag, tages der i stedet en eksklusiv (X) lås.

Dette tyder på, at tipkombinationen burde resultere i en enkelt eksklusiv bordlås. Faktisk er dette ikke helt hele historien:

I SQL Server 2000 resulterer kombination af UPDLOCK og TABLOCK-tip i, at Tab-S (en delt tabellås) tages efterfulgt af konvertering til Tab-X (eksklusiv tabellås) under alle isolationsniveauer undtagen READ UNCOMMITTED. Denne sekvens af låse kan resultere i en dødvande, hvor tre eller flere sessioner er involveret:to sessioner erhverver Tab-S og begge venter på den anden for at konvertere til Tab-X. Under READ UNCOMMITTED tager SQL Server 2000 Sch-S og derefter Tab-X, som ikke er tilbøjelig til deadlock (kun normal blokering).

I SQL Server 2005 og frem (uden fejlrettelsen) afhænger låsene kun om RCSI er aktiveret eller ej. Hvis RCSI er aktiveret, alle isolationsniveauer tag Tab-IX og konverter derefter til Tab-X. Denne sekvens forårsager den deadlock, fejlrettelsen adresserer.

Hvis RCSI ikke er aktiveret, opfører de matchende isolationsniveauer sig, som de gjorde under SQL Server 2000 (ved at tage Tab-S og derefter konvertere til Tab-X). Det (nye for 2005) snapshot-isolationsniveau tager Sch-S efterfulgt af Tab-X. Som en konsekvens heraf er SI og READ UNCOMMITTED de eneste isolationsniveauer, der ikke er tilbøjelige til denne deadlock i UPDLOCK, TABLOCK scenariet, når RCSI ikke er aktiveret.

The Deadlock Fix

Rettelsen ændrer de låse, der tages, når UPDLOCK og TABLOCK er specificeret sammen, for alle isolationsniveauer , og uanset om RCSI er aktiveret eller ej. Efter rettelsen er anvendt, får UPDLOCK og TABLOCK motoren til at erhverve Tab-SIX (tabel-niveau delt med hensigt udelukkende), som derefter konverteres til Tab-X.

Dette undgår deadlock-scenariet, fordi Tab-SIX er inkompatibelt med en anden Tab-SIX. Husk, at dødvandet opstod, da to processer holdt Tab-IX og ventede på at konvertere til Tab-X. Med Tab-IX erstattet af Tab-SIX, er det ikke muligt for begge at holde Tab-SIX på samme tid. Resultatet er et normalt blokeringsscenarie i stedet for et dødvande.

Sidste tanker

Den "forkerte deadlock" rettelse løser et bestemt deadlock-scenarie, men det resulterer stadig ikke i den adfærd, jeg forestiller mig, at de personer, der specificerer UPDLOCK og TABLOCK, har forudset. Hvis SQL Server havde en Tab-U-lås (opdatering på tabelniveau), ville den forhindre samtidige ændringer af tabellen, men tillade samtidige læsere. Det er, hvad jeg forestiller mig, at hensigten med folk, der bruger disse tip sammen, ville være, og jeg kan se, hvordan det kan være nyttigt.

Den nuværende implementering (hvor Tab-X i sidste ende tages i stedet for den manglende Tab-U) matcher ikke denne forventning, fordi Tab-X forhindrer samtidige læsninger (medmindre der bruges et rækkeversionsisolationsniveau). Vi kan lige så godt angive TABLOCKX i mange tilfælde. Det faktum, at rettelsen også introducerer en ny fejl (kun for brugere af SQL Server 2008 R2) er også uheldigt, især hvis fejlen fortsætter med at blive inkluderet i 2008 R2 SP3.

Bemærk, at deadlock-rettelsen ikke gøres tilgængelig for SQL Server-versioner før 2008 R2. Disse versioner vil fortsat have den komplekse låseadfærd for UPDLOCK og TABLOCK som beskrevet ovenfor.

Tak til Eugene Karpovich, som først gjorde mig opmærksom på dette problem i en kommentar til min artikel om dataændringer under RCSI.


  1. Hvad er forskellen mellem at bruge en krydssammenføjning og at sætte et komma mellem de to tabeller?

  2. Java + Mysql UTF8-problem

  3. Tilfældig registrering fra en databasetabel (T-SQL)

  4. SQL Server 2005 Hvordan opretter man en unik begrænsning?