sql >> Databasteknik >  >> RDS >> Database

ACID-egenskaperna för uttalanden och transaktioner

[ Se indexet för hela serien ]

Vilken programmerare som helst kommer att berätta för dig att det kan vara svårt att skriva säker flertrådig kod. Det kräver stor omsorg och en god förståelse för de tekniska frågorna. Som databasperson kanske du tror att den här typen av svårigheter och komplikationer inte gäller när du skriver T-SQL. Så det kan komma som lite av en chock att inse att T-SQL-kod också är sårbar för den typ av tävlingsförhållanden och andra dataintegritetsrisker som oftast förknippas med flertrådsprogrammering. Detta är sant oavsett om vi talar om en enskild T-SQL-sats eller en grupp av satser inneslutna i en explicit transaktion.

Kärnan i problemet är det faktum att databassystem tillåter flera transaktioner att utföras samtidigt. Detta är ett välkänt (och mycket önskvärt) tillstånd, men en stor del av T-SQL-produktionskoden antar fortfarande tyst att de underliggande data inte ändras under utförandet av en transaktion eller en enda DML-sats som SELECT , INSERT , UPDATE , DELETE , eller MERGE .

Även där kodförfattaren är medveten om de möjliga effekterna av samtidiga dataändringar, antas användningen av explicita transaktioner alltför ofta ge mer skydd än vad som faktiskt är motiverat. Dessa antaganden och missuppfattningar kan vara subtila och är säkerligen kapabla att vilseleda även erfarna databasutövare.

Nu finns det fall där dessa frågor inte spelar någon större roll i praktisk mening. Till exempel kan databasen vara skrivskyddad, eller så kan det finnas någon annan äkta garanti att ingen annan kommer att ändra den underliggande datan medan vi arbetar med den. Likaså kanske operationen i fråga inte kräver resultat som är exakt korrekt; våra datakonsumenter kan vara helt nöjda med ett ungefärligt resultat (även ett som inte representerar det engagerade tillståndet för databasen på någon tidpunkt).

Samtidighetsproblem

Frågan om interferens mellan aktiviteter som körs samtidigt är ett välbekant problem för applikationsutvecklare som arbetar i programmeringsspråk som C# eller Java. Lösningarna är många och varierande, men innebär generellt att man använder atomära operationer eller skaffa en ömsesidigt exklusiv resurs (som ett lås ) medan en känslig operation pågår. Om lämpliga försiktighetsåtgärder inte vidtas är de troliga resultaten korrupta data, ett fel eller kanske till och med en fullständig krasch.

Många av samma begrepp (t.ex. atomoperationer och lås) finns i databasvärlden, men tyvärr har de ofta avgörande skillnader i betydelse . De flesta databasmänniskor är medvetna om ACID-egenskaperna hos databastransaktioner, där A:et står för atomic . SQL Server använder också lås (och andra enheter för ömsesidig uteslutning internt). Ingen av dessa termer betyder riktigt vad en erfaren C#- eller Java-programmerare rimligen kan förvänta sig, och många databasproffs har också en förvirrad förståelse för dessa ämnen (vilket en snabb sökning med din favoritsökmotor kommer att vittna om).

För att upprepa, ibland kommer dessa frågor inte att vara ett praktiskt problem. Om du skriver en fråga för att räkna antalet aktiva beställningar i ett databassystem, hur viktigt är det om räkningen är lite av? Eller om det återspeglar tillståndet för databasen vid någon annan tidpunkt?

Det är vanligt att riktiga system gör en avvägning mellan samtidighet och konsistens (även om designern inte var medveten om det vid den tidpunkten – informerad avvägningar är kanske ett mer sällsynt djur). Riktiga system fungerar ofta tillräckligt bra , med eventuella anomalier kortlivade eller betraktas som oviktiga. En användare som ser ett inkonsekvent tillstånd på en webbsida kommer ofta att lösa problemet genom att uppdatera sidan. Om problemet rapporteras kommer det troligen att stängas som Ej reproducerbart. Jag säger inte att detta är ett önskvärt tillstånd, bara att inse att det händer.

Ändå är det oerhört användbart att förstå samtidighetsfrågor på en grundläggande nivå. Att vara medveten om dem gör det möjligt för oss att skriva korrekt (eller informerat). korrekt-tillräckligt) T-SQL som omständigheterna kräver. Ännu viktigare, det tillåter oss att undvika att skriva T-SQL som kan äventyra den logiska integriteten hos våra data.

Men SQL Server ger ACID-garantier!

Ja, det gör det, men de är inte alltid vad man kan förvänta sig, och de skyddar inte allt. Oftare än inte läser människor mycket mer på ACID än vad som är motiverat.

De mest missförstådda komponenterna i ACID-akronymen är orden Atomic, Consistent och Isolated – vi kommer till dem om ett ögonblick. Den andra, Durable , är tillräckligt intuitivt så länge du kommer ihåg att det bara gäller persistent (återställbar) användare data.

Med allt detta sagt, börjar SQL Server 2014 att sudda ut gränserna för Durable-egenskapen något med införandet av allmän fördröjd hållbarhet och OLTP-schema-bara hållbarhet i minnet. Jag nämner dem bara för fullständighetens skull, vi kommer inte att diskutera dessa nya funktioner ytterligare. Låt oss gå vidare till de mer problematiska ACID-egenskaperna:

Atomegenskapen

Många programmeringsspråk tillhandahåller atomära operationer som kan användas för att skydda mot tävlingsförhållanden och andra oönskade samtidiga effekter, där flera exekveringstrådar kan komma åt eller ändra delade datastrukturer. För applikationsutvecklaren kommer en atomär operation med en uttrycklig garanti för fullständig isolering från effekterna av annan samtidig bearbetning i ett flertrådigt program.

En analog situation uppstår i databasvärlden, där flera T-SQL-frågor samtidigt får åtkomst till och modifierar delad data (dvs databasen) från olika trådar. Observera att vi inte talar om parallella frågor här; vanliga enkeltrådade frågor är rutinmässigt schemalagda att köras samtidigt inom SQL Server på separata arbetstrådar.

Tyvärr, den atomära egenskapen av SQL-transaktioner garanterar endast att dataändringar som utförs inom en transaktion lyckas eller misslyckas som en enhet . Inget mer än så. Det finns verkligen ingen garanti för fullständig isolering från effekterna av annan samtidig behandling. Lägg också märke till i förbigående att atomär transaktionsegenskap inte säger något om några garantier för läsning data.

Enstaka uttalanden

Det är inte heller något speciellt med ett enskilt uttalande i SQL Server. Där en explicit innehållande transaktion (BEGIN TRAN...COMMIT TRAN ) inte existerar, en enda DML-sats körs fortfarande inom en autocommit-transaktion. Samma ACID-garantier gäller för ett enda uttalande, och samma begränsningar också. I synnerhet kommer ett enstaka uttalande utan särskilda garantier för att data inte kommer att ändras medan det pågår.

Tänk på följande leksaksfråga i AdventureWorks:

SELECT
    TH.TransactionID,
    TH.ProductID,
    TH.ReferenceOrderID,
    TH.ReferenceOrderLineID,
    TH.TransactionDate,
    TH.TransactionType,
    TH.Quantity,
    TH.ActualCost
FROM Production.TransactionHistory AS TH
WHERE TH.ReferenceOrderID =
(
    SELECT TOP (1) 
        TH2.ReferenceOrderID
    FROM Production.TransactionHistory AS TH2
    WHERE TH2.TransactionType = N'P'
    ORDER BY 
        TH2.Quantity DESC,
        TH2.ReferenceOrderID ASC
);

Frågan är avsedd att visa information om beställningen som rankas först efter kvantitet. Utförandeplanen är följande:

Huvudåtgärderna i denna plan är:

  1. Skanna tabellen för att hitta rader med önskad transaktionstyp
  2. Hitta det order-ID som sorterar högst enligt specifikationen i underfrågan
  3. Hitta raderna (i samma tabell) med det valda order-ID:t med hjälp av ett icke-klustrat index
  4. Slå upp återstående kolumndata med det klustrade indexet

Föreställ dig nu att en samtidig användare ändrar Order 495, ändrar dess Transaktionstyp från P till W, och överför den ändringen till databasen. Som tur är går den här ändringen igenom medan vår fråga utför sorteringsoperationen (steg 2).

När sorteringen är klar hittar indexsökningen i steg 3 raderna med det valda order-ID (som råkar vara 495) och nyckelsökningen i steg 4 hämtar de återstående kolumnerna från bastabellen (där Transaktionstypen nu är W) .

Denna händelseförlopp innebär att vår fråga ger ett till synes omöjligt resultat:

Istället för att hitta order med transaktionstyp P som den angivna frågan, visar resultaten transaktionstyp W.

Grundorsaken är tydlig:vår fråga antog implicit att data inte kunde ändras medan vår fråga med ett påstående pågick. Möjlighetsfönstret i det här fallet var relativt stort på grund av blockeringssorteringen, men samma sorts rastillstånd kan uppstå i vilket skede som helst av exekveringen av en fråga, generellt sett. Naturligtvis är riskerna vanligtvis högre med ökade nivåer av samtidiga ändringar, större tabeller och där blockerande operatörer förekommer i frågeplanen.

En annan ihärdig myt i samma allmänna område är att MERGE är att föredra framför separat INSERT , UPDATE och DELETE satser eftersom enkelsatsen MERGE är atomär. Det är såklart nonsens. Vi kommer att återkomma till den här typen av resonemang längre fram i serien.

Det allmänna meddelandet vid denna tidpunkt är att om inte explicita åtgärder vidtas för att säkerställa annat, kan datarader och indexposter ändras, flytta position eller försvinna helt när som helst under exekveringsprocessen. En mental bild av konstant och slumpmässig förändring i databasen är bra att ha i åtanke när du skriver T-SQL-frågor.

Konsistensegenskapen

Det andra ordet från ACID-akronymen har också en rad möjliga tolkningar. I en SQL Server-databas betyder Konsistens endast att en transaktion lämnar databasen i ett tillstånd som inte bryter mot några aktiva begränsningar. Det är viktigt att till fullo förstå hur begränsat det påståendet är:De enda ACID-garantierna för dataintegritet och logisk konsistens är de som tillhandahålls av aktiva begränsningar.

SQL Server tillhandahåller ett begränsat antal begränsningar för att upprätthålla logisk integritet, inklusive PRIMARY KEY , FOREIGN KEY , CHECK , UNIQUE , och NOT NULL . Dessa är alla garanterade uppfyllda vid den tidpunkt då en transaktion genomförs. Dessutom garanterar SQL Server det fysiska databasens integritet hela tiden, naturligtvis.

De inbyggda begränsningarna är inte alltid tillräckliga för att upprätthålla alla affärs- och dataintegritetsregler vi vill ha. Det är förvisso möjligt att vara kreativ med standardfaciliteterna, men dessa blir snabbt komplexa och kan resultera i lagring av duplicerad data.

Som en konsekvens innehåller de flesta riktiga databaser åtminstone några T-SQL-rutiner skrivna för att upprätthålla ytterligare regler, till exempel i lagrade procedurer och triggers. Ansvaret för att säkerställa att den här koden fungerar korrekt ligger helt och hållet på författaren – egenskapen Konsistens ger inga specifika skydd.

För att understryka poängen måste pseudo-begränsningar skrivna i T-SQL fungera korrekt oavsett vilka samtidiga ändringar som kan inträffa. En applikationsutvecklare kan skydda en känslig operation som den med en låssats. Det närmaste T-SQL-programmerare har den möjligheten för att lagra procedur och triggerkod i riskzonen är den jämförelsevis sällan använda sp_getapplock systemlagrad procedur. Det betyder inte att det är det enda, eller ens föredragna alternativet, bara att det finns och kan vara det rätta valet under vissa omständigheter.

Isolationsegenskapen

Detta är lätt det mest missförstådda av ACID-transaktionsegenskaperna.

I princip en helt isolerad transaktionen körs som den enda uppgiften som körs mot databasen under dess livstid. Andra transaktioner kan bara starta när den aktuella transaktionen har slutförts helt (d.v.s. genomförd eller återställd). Utförd på detta sätt skulle en transaktion verkligen vara en atomär operation , i strikt mening som en icke-databasperson skulle tillskriva frasen.

I praktiken fungerar databastransaktioner istället med en grad av isolering specificeras av den för närvarande effektiva transaktionsisoleringsnivån (vilket gäller lika för fristående uttalanden, kom ihåg). Denna kompromiss (graden av isolering) är den praktiska konsekvensen av avvägningarna mellan samtidighet och korrekthet som nämnts tidigare. Ett system som bokstavligen behandlade transaktioner en i taget, utan överlappning i tid, skulle ge fullständig isolering men den totala systemgenomströmningen skulle sannolikt vara dålig.

Nästa gång

Nästa del i den här serien kommer att fortsätta undersökningen av samtidighetsproblem, ACID-egenskaper och transaktionsisolering med en detaljerad titt på den serialiserbara isoleringsnivån, ett annat exempel på något som kanske inte betyder vad du tror att det gör.

[ Se indexet för hela serien ]


  1. hur man emulerar insert ignore och på duplicate key update (sql merge) med postgresql?

  2. Hur du justerar prestanda för SQL Server, Azure SQL Database och Amazon RDS

  3. Lagring av SHA1-hashvärden i MySQL

  4. Oracle PL/SQL:Dynamiskt SQL-exempel med Execute Immediate