sql >> Databasteknik >  >> RDS >> Database

Den serialiserbara isoleringsnivån

[ Se indexet för hela serien ]

Mycket produktions-T-SQL-kod skrivs med det implicita antagandet att de underliggande data inte kommer att förändras under exekvering. Som vi såg i den tidigare artikeln i den här serien är detta ett osäkert antagande eftersom data och indexposter kan röra sig under oss, även under exekvering av ett enstaka uttalande.

Där T-SQL-programmeraren är medveten om de typer av korrekthets- och dataintegritetsproblem som kan uppstå på grund av samtidiga datamodifieringar av andra processer, är den vanligaste lösningen att linda de sårbara uttalandena i en transaktion. Det är inte klart hur samma sorts resonemang skulle tillämpas på fallet med enstaka påståenden, som redan är insvept i en automatisk commit-transaktion som standard.

Om man lämnar det åt sidan för en sekund, verkar idén att skydda ett viktigt område av T-SQL-kod med en transaktion vara baserad på en missuppfattning om skyddet som erbjuds av ACID-transaktionsegenskaperna. Den viktiga delen av den förkortningen för den aktuella diskussionen är Isoleringen fast egendom. Tanken är att användning av en transaktion automatiskt ger fullständig isolering från effekterna av andra samtidiga aktiviteter.

Sanningen i saken är att transaktioner under SERIALIZABLE ge bara en examen av isolering, vilket beror på den för närvarande effektiva transaktionsisoleringsnivån. För att förstå vad allt detta betyder för vår vardagliga T SQL-kodningsmetoder kommer vi först att ta en detaljerad titt på den serialiserbara isoleringsnivån.

Serialiserbar isolering

Serialiserbar är den mest isolerade av standardtransaktionsisoleringsnivåerna. Det är också standard isoleringsnivå specificerad av SQL-standarden, även om SQL Server (som de flesta kommersiella databassystem) skiljer sig från standarden i detta avseende. Standardisoleringsnivån i SQL Server är read committed, en lägre isoleringsnivå som vi kommer att utforska senare i serien.

Definitionen av den serialiserbara isoleringsnivån i SQL-92-standarden innehåller följande text (betoning min):

En serialiserbar exekvering definieras som en exekvering av operationerna för att samtidigt köra SQL-transaktioner som ger samma effekt som viss seriell exekvering av samma SQL-transaktioner. En seriell exekvering är en där varje SQL-transaktion körs till slut innan nästa SQL-transaktion börjar.

Det finns en viktig skillnad att göra här mellan verkligen serialiserad exekvering (där varje transaktion faktiskt körs uteslutande tills den är klar innan nästa startar) och serialiserbar isolering, där transaktioner bara krävs för att ha samma effekter som om de utfördes i serie (i någon ospecificerad ordning).

För att uttrycka det på ett annat sätt, ett riktigt databassystem tillåts fysiskt överlappa utförandet av serialiserbara transaktioner i tid (vilket ökar samtidigheten) så länge som effekterna av dessa transaktioner fortfarande motsvarar en möjlig seriell ordningsföljd. Med andra ord, serialiserbara transaktioner är potentiellt serialiserbara snarare än att faktiskt serialiseras .

Logiskt serialiserbara transaktioner

Lämna alla fysiska överväganden (som låsning) åt sidan för ett ögonblick och tänk bara på den logiska behandlingen av två parallella serialiserbara transaktioner.

Tänk på en tabell som innehåller ett stort antal rader, varav fem råkar uppfylla ett intressant frågepredikat. En serialiserbar transaktion T1 börjar räkna antalet rader i tabellen som matchar detta predikat. En tid efter T1 börjar, men innan den genomförs, en andra serialiserbar transaktion T2 startar. Transaktion T2 lägger till fyra nya rader som också uppfyller frågepredikatet till tabellen och commits. Diagrammet nedan visar tidsförloppet för händelser:

Frågan är, hur många rader ska frågan i serialiserbar transaktion T1 räkna? Kom ihåg att vi bara tänker på de logiska kraven här, så undvik att tänka på vilka lås som kan tas och så vidare.

De två transaktionerna överlappar varandra fysiskt i tid, vilket är bra. Serialiserbar isolering kräver bara att resultaten av dessa två transaktioner motsvarar en möjlig seriell exekvering. Det finns helt klart två möjligheter för ett logiskt seriellt schema för transaktioner T1 och T2 :

Använder det första möjliga serieschemat (T1 sedan T2 ) T1 räkningsfrågan skulle se fem rader , eftersom den andra transaktionen inte startar förrän den första är klar. Genom att använda det andra möjliga logiska schemat, T1 fråga skulle räkna nio rader , eftersom insättningen med fyra rader logiskt slutfördes innan räknetransaktionen började.

Båda svaren är logiskt korrekta under serialiserbar isolering. Dessutom är inget annat svar möjligt (så transaktion T1 kunde till exempel inte räkna sju rader). Vilket av de två möjliga resultaten som faktiskt observeras beror på exakt timing och ett antal implementeringsdetaljer som är specifika för den databasmotor som används.

Observera att vi inte drar slutsatsen att transaktionerna faktiskt på något sätt ordnas om i tid. Den fysiska exekveringen är fri att överlappa som visas i det första diagrammet, så länge som databasmotorn säkerställer att resultaten återspeglar vad som skulle ha hänt om de hade körts i en av de två möjliga seriesekvenserna.

Serialiserbar och samtidighetsfenomenen

Förutom logisk serialisering nämner SQL-standarden också att en transaktion som fungerar på den serialiserbara isoleringsnivån inte får uppleva vissa samtidighetsfenomen. Den får inte läsa oengagerad data (inga smutsiga läsningar ); och när data väl har lästs måste en upprepning av samma operation returnera exakt samma datauppsättning (repeterbara läsningar utan fantomer ).

Standarden gör en poäng med att säga att dessa samtidighetsfenomen exkluderas på den serialiserbara isoleringsnivån som en direkt konsekvens att kräva att transaktionen är logiskt serialiserbar. Med andra ord är serialiseringskravet tillräckligt i sig för att undvika den smutsiga läsningen, icke-repeterbar läsning och fenomenen fantom samtidighet. Däremot är det inte tillräckligt att undvika enbart de tre samtidiga fenomenen för att garantera serialisering, som vi kommer att se inom kort.

Intuitivt undviker serialiserbara transaktioner alla samtidighetsrelaterade fenomen eftersom de måste agera som om de hade utförts helt isolerat. I den meningen matchar den serialiserbara transaktionsisoleringsnivån de vanliga förväntningarna hos T-SQL-programmerare ganska nära.

Serialiserbara implementeringar

SQL Server råkar använda en låsimplementering av den serialiserbara isoleringsnivån, där fysiska lås förvärvas och hålls till slutet av transaktionen (därav den föråldrade tabelltipset HOLDLOCK som en synonym för SERIALIZABLE ).

Denna strategi räcker inte riktigt för att ge en teknisk garanti för full serialiserbarhet, eftersom nya eller ändrade data kan dyka upp i en rad rader som tidigare bearbetats av transaktionen. Detta samtidighetsfenomen är känt som ett fantom och kan resultera i effekter som inte kunde ha inträffat i något seriellt schema.

För att säkerställa skydd mot fenomenet phantom concurrency kan lås som tas av SQL Server på den serialiserbara isoleringsnivån också innehålla nyckelintervallslåsning för att förhindra att nya eller ändrade rader visas mellan tidigare granskade indexnyckelvärden. Räckviddslås är inte alltid förvärvad under den serialiserbara isoleringsnivån; allt vi kan säga i allmänhet är att SQL Server alltid skaffar tillräckligt med lås för att uppfylla de logiska kraven för den serialiserbara isoleringsnivån. Faktum är att låsimplementeringar ganska ofta får fler och strängare lås än vad som verkligen behövs för att garantera serialisering, men jag avviker.

Låsning är bara en av de möjliga fysiska implementeringarna av den serialiserbara isoleringsnivån. Vi bör vara noga med att mentalt skilja de specifika beteendena hos SQL Server-låsningsimplementeringen från den logiska definitionen av serialiserbar.

Som ett exempel på en alternativ fysisk strategi, se PostgreSQL-implementeringen av serialiserbar ögonblicksbildsisolering, även om detta bara är ett alternativ. Varje annan fysisk implementering har sina egna styrkor och svagheter såklart. För övrigt, notera att Oracle fortfarande inte tillhandahåller en helt kompatibel implementering av den serialiserbara isoleringsnivån. Den har en isoleringsnivå namngiven serialiserbar, men det garanterar inte riktigt att transaktioner kommer att utföras enligt något möjligt seriellt schema. Oracle tillhandahåller istället ögonblicksbildsisolering när serialiserbar begärs, ungefär på samma sätt som PostgreSQL gjorde innan serialiserbar ögonblicksbildsisolering (SSI ) implementerades.

Snapshot-isolering förhindrar inte samtidiga anomalier som skrivskev, vilket inte är möjligt under verkligt serialiserbar isolering. Om du är intresserad kan du hitta exempel på skrivskev och andra samtidiga effekter som tillåts av ögonblicksbildsisolering på SSI-länken ovan. Vi kommer också att diskutera SQL Server-implementeringen av snapshot-isoleringsnivån senare i serien.

En punkt-i-tid-vy?

En anledning till att jag har ägnat tid åt att prata om skillnaderna mellan logisk serialiserbarhet och fysiskt serialiserad exekvering är att det annars är lätt att sluta sig till garantier som kanske inte faktiskt existerar. Till exempel, om du tänker på serialiserbara transaktioner som faktiskt exekvera en efter en, kan du dra slutsatsen att en serialiserbar transaktion nödvändigtvis kommer att se databasen som den fanns i början av transaktionen, vilket ger en punkt-i-tidsvy.

I själva verket är detta en implementeringsspecifik detalj. Kom ihåg föregående exempel, där serialiserbar transaktion T1 kan legitimt räkna fem eller nio rader. Om ett antal av nio returneras, ser den första transaktionen tydligt rader som inte fanns när transaktionen startade. Detta resultat är möjligt i SQL Server men inte i PostgreSQL SSI, även om båda implementeringarna överensstämmer med de logiska beteenden som anges för den serialiserbara isoleringsnivån.

I SQL Server ser serialiserbara transaktioner inte nödvändigtvis informationen som den fanns i början av transaktionen. Snarare innebär detaljerna i SQL Server-implementeringen att en serialiserbar transaktion ser de senaste committed data, från det ögonblick då data först låstes för åtkomst. Dessutom garanteras att uppsättningen av senast läsbara data inte ändrar medlemskap innan transaktionen avslutas.

Nästa gång

Nästa del i den här serien undersöker den repeterbara läsisoleringsnivån, som ger svagare transaktionsisoleringsgarantier än serialiserbar.

[ Se indexet för hela serien ]


  1. Android SQLiteOpenHelper:Varför kallas inte metoden onCreate()?

  2. Vad är nytt i PgBouncer 1.6

  3. Hur Log() fungerar i PostgreSQL

  4. SQL:hur man använder UNION och beställer efter ett specifikt urval?