Kapittel 8 · 8C · Forelesning 16

Recovery — livet etter krasj

Notater kap. 18. Loggens jobb: garantere atomicity og durability uansett hva som skjer — strømbrudd, OS-kill, deadlock-abort.

01 · Mål

Hva skal recovery levere?

Recovery manager har bare ett oppdrag: gjenopprette databasen til en konsistent tilstand etter en feil — og det med to ACID-bokstaver i bagasjen, A og D.

  • Atomicity — alle uavsluttede transaksjoner ved krasj skal rulles tilbake (undo).
  • Durability — alle committed transaksjoner ved krasj skal være på disken etterpå (redo om nødvendig).

I tillegg må recovery-prosessen være idempotent: hvis maskinen krasjer igjen midt under recovery, skal det å starte recovery på nytt fortsatt gi riktig resultat. Det er CLR-ene (kapittel 06 her) som gjør dette mulig.

Tre typer feil

  1. Transaksjons-abort under normal drift — manglende data, brukervalg, deadlock.
  2. System-krasj — strømbrudd, OS-kill, software-feil. RAM forsvinner, disk overlever.
  3. Mediefeil — disken selv er ødelagt. Trenger backup + redo. (Utenfor pensum.)
02 · Vinnere og tapere

Hvilke transaksjoner var egentlig ferdige?

Når strømmen gikk, var noen transaksjoner committed (vi vet det fordi commit-loggrecorden var på disk), andre var midt i. De første kalles winners, de andre losers.

Tid → CRASH T1 commit ✓ T2 commit ✓ T3 commit ✓ T4 løs — undo T5 løs — undo winner (committed) loser (in-flight)
T1, T2, T3 fikk commit-loggrecord på disk før krasj — de er winners. T4 og T5 hadde ikke det — løsere som må rulles tilbake.

Etter krasj må recovery-prosessen:

  1. Finne ut hvem som var winner og hvem som var loser (Analysis-fasen).
  2. Sørge for at alle winners' endringer er på disk (Redo-fasen).
  3. Rulle tilbake alle losers' delvise endringer (Undo-fasen).
Sjekk · Vinnere
En transaksjon har skrevet en commit log-record til log-bufferen i RAM, men maskinen krasjer før loggbufferen blir tømt til disk. Er den winner eller loser?
AWinner — den prøvde å committe
BLoser — committen var ikke på stabil lagring (disk) ved krasjet
CDen blir spart ved at vi henter loggen fra RAM
DAvhenger av isolation level
Loser. RAM forsvinner ved krasj. Vi vet kun om transaksjoner som har commit-record på disk. Det er nettopp derfor «force-log-at-commit» finnes — for å gjøre commit-loggen synkron med disk og sikre durability.
03 · Buffer-policy

Force og steal

Hvilken algoritme et DBMS trenger avhenger av to designvalg om buffer-poolen:

  • Force: Tvinger systemet at alle data-sider skrives til disk ved commit?
    • Ja (force) — durability lett, men dyr commit (mange random writes).
    • Nei (no-force) — billig commit, men trenger redo etter krasj.
  • Steal: Kan en buffer-slot stjeles fra en uavsluttet transaksjon (skrives til disk)?
    • Ja (steal) — fleksibel buffer-bruk, men trenger undo for halvskrevne sider.
    • Nei (no-steal) — uavsluttede sider holdes i buffer til commit. Begrenset.
No-stealSteal
ForceShadowing (ingen logg)Bare undo-logg
No-forceBare redo-loggUndo + redo (ARIES)

ARIES sitter i steal/no-force-hjørnet: maksimal fleksibilitet for buffer manager, men krever både undo- og redo-logging. Det er prisen for ytelse i moderne SQL-databaser.

04 · Loggen

Append-only kronologi

Loggen er en sekvensiell, append-only fil på en egen disk. Hvert log-record har en monotont voksende LSN (Log Sequence Number).

Generisk log-record

(LSN, TransID, PrevLSN, OpType, PageID, Offset, BeforeImage, AfterImage)
  • LSN — unik, voksende. Også brukt som PageLSN på datasiden.
  • PrevLSN — peker bakover til forrige log-record fra samme transaksjon. Lager en lenket kjede per Tx.
  • OpType — update / insert / delete / commit / abort / CLR / checkpoint.
  • BeforeImage / AfterImage — hva som lå der før og etter (brukes til hhv. undo og redo).

PageLSN — koblingen mellom logg og data

Hver dataside har et felt PageLSN som inneholder LSN-en til siste log-record som ble brukt på siden. Det er den sjekken redo-fasen bruker for å avgjøre om en endring allerede er reflektert på siden.

Log buffer (RAM) LSN 100 LSN 101 LSN 102 LSN 103 LSN 104 FlushedLSN = 102 Log på disk: LSN 0..102 Dataside i buffer PageID: A PageLSN: 102 Innhold: … Database-disk
FlushedLSN = nyeste log-record på disk. PageLSN på dataside = LSN av siste endring på siden. Ved å sammenlikne kan vi vite om en endring trenger redo.
05 · WAL

Write-ahead logging

Hele recovery-arkitekturen hviler på én regel:

WAL-regelen: Før en endret dataside skrives til disk, må alle log-records som beskriver endringen være på disk først.

Konkret: før vi skriver dirty side P med PageLSN = L til disk, må FlushedLSN ≥ L. Hvis ikke, kunne et krasj rett etter side-skrivingen latt oss stå med en delvis oppdatert side på disk uten noen log-record som forteller hvordan vi skulle rulle den tilbake — atomicity brutt.

WAL har egentlig to deler:

  1. Undo-WAL — log først, data etterpå (ovenfor). Sikrer atomicity.
  2. Force-log-at-commit — ved COMMIT, tving log-bufferen til disk før suksess returneres til klient. Sikrer durability.

Log-buffer

100: w(A, …) ✓ disk
101: w(B, …) ✓ disk
102: w(C, …) ⚡ ny

Buffer-pool

side A · PageLSN=100
side B · PageLSN=101
side C · PageLSN=102 (dirty!)

Disk

log: 0..101
side A · på disk
side B · på disk
side C · gammel versjon

Side C kan ikke skrives til disk før log-record 102 er på disk (FlushedLSN ≥ 102).

Sjekk · WAL
Hvorfor er WAL-regelen «logg først, data etterpå» — ikke omvendt?
ALoggen er kortere enn dataen
BHvis vi krasjer, må vi kunne rulle siden tilbake — det krever before-image fra loggen
CDisken bruker mindre energi på sekvensielle skrivinger
DSQL-standarden krever det
Atomicity-argumentet. Hvis siden gikk på disk uten loggen, og vi krasjet, ville vi sittet med halvskrevne sider på disken og ingen måte å rulle dem tilbake. Loggen ligge på disk først, slik at vi alltid har before-image om vi trenger å undo.
06 · CLR

Compensating Log Records

Tenk: under undo for en aborted transaksjon krasjer maskinen. Når den starter på nytt — hvor langt har vi kommet i å rulle tilbake? Skal vi rulle tilbake samme record igjen?

ARIES sin eleganse: når vi undoer en log-record, skriver vi en ny log-record som beskriver «jeg har angret den endringen». Den heter en CLR (Compensating Log Record). PageLSN på siden oppdateres til CLR-ens LSN.

Egenskaper:

  • CLR-er er redo-only — de undoes aldri.
  • De har et UndoNextLSN-felt som peker til det neste som skal undoes (forbi den vi nettopp angret).
  • Hvis krasj skjer under undo, kan vi gjenkjenne CLR-er ved restart og hoppe rett til der vi var.

Resultat: idempotent recovery. Du kan krasje, restarte, krasje igjen — slutt-tilstanden er den samme.

07 · Datastrukturer

Det ARIES holder rede på

To tabeller i RAM (kopieres til logg ved sjekkpunkter):

Transaction Table (TT)

Én rad per aktiv transaksjon. Inneholder:

  • TransID
  • Status — active / committed / aborted
  • LastLSN — siste log-record skrevet av denne Tx (start på undo-kjeden)

Dirty Page Table (DPT)

Én rad per dirty side i buffer (modifisert, men ikke skrevet til disk). Inneholder:

  • PageID
  • RecLSN — LSN av første log-record som gjorde siden dirty etter siste flush. Det er det tidligste punktet redo må gå tilbake til for denne siden.

Hvorfor skiller vi RecLSN fra PageLSN? Begge gjelder samme side, men ulike formål: RecLSN sier hvor langt tilbake redo må starte; PageLSN sier hvor langt redo allerede er kommet.

Sjekk · Datastrukturer
Hva skjer med en sides oppføring i DPT når siden flushes til disk?
ARecLSN settes lik PageLSN
BOppføringen fjernes — siden er ikke lenger dirty
COppføringen merkes "clean" men beholdes
DIngenting endrer seg
Fjernes. DPT inneholder kun sider som har endringer i buffer som ikke er på disk. Når flushen er ferdig, er bufferen og disken synkronisert, og siden tas ut. Hvis den senere skrives på igjen, kommer den tilbake i DPT med en ny RecLSN.
08 · ARIES

Tre faser ved restart

Etter krasj går recovery-prosessen gjennom nøyaktig tre faser, alltid i denne rekkefølgen. Klikk «Spill av» for å se rekkefølgen:

FASE 1

Analysis

Skann logg fra siste sjekkpunkt og fremover. Bygg opp Transaction Table og Dirty Page Table slik de var ved krasjet. Identifiser winners (committed) og losers (in-flight).

FASE 2

Redo

Start ved minste RecLSN i DPT. Skann fremover. For hver log-record: utfør den om PageLSN < LSN. Repeating history — alle endringer (også fra losere!) gjenspilles.

FASE 3

Undo

baklengs gjennom log-recordene til losers (via PrevLSN). For hver, skriv en CLR og angre endringen. Når alle losers er rullet helt tilbake — ferdig.

Hver fase tar ca. 1.4s i animasjonen.
Snikende detalj — repeating history

Redo gjenspiller alt, også endringer gjort av løsere. Hvorfor? Fordi PageLSN-sjekken bruker LSN-sammenlikning, og fordi det forenkler logikken: når vi når undo-fasen, er databasen i nøyaktig samme tilstand som ved krasjet (inkludert halvgjorte endringer fra losere). Så undo må bare reverse log-recordene, uansett hva som har skjedd.

09 · Eksempel

Analysis i praksis

La A, B, C være data-sider. Etter krasj finner vi følgende logg:

LSNPrevLSNTxOpTypePage
1010T1UpdateC
1020T2UpdateB
103101T1Commit
104Begin_ckpt
105End_ckpt (TT, DPT)
1060T3UpdateA
107102T2UpdateC
108107T2Commit

Ved sjekkpunkt (LSN 105) hadde TT og DPT følgende:

TT @ ckpt

TxLastLSNStatus
T1103Commit
T2102Active

DPT @ ckpt

PageRecLSN
C101
B102

Etter Analysis

Vi skanner forover fra LSN 106:

  • LSN 106 (T3, Update A) — T3 er ny i TT, status active, lastLSN=106. A er ny i DPT, RecLSN=106.
  • LSN 107 (T2, Update C) — T2 sin lastLSN oppdateres til 107. C eksisterer allerede i DPT med RecLSN=101 — vi oppdaterer ikke RecLSN (vi vil ha eldste!).
  • LSN 108 (T2, Commit) — T2 sin status blir committed.

TT etter analysis

TxLastLSNStatus
T1103Commit
T2108Commit
T3106Active → undo!

DPT etter analysis

PageRecLSN
C101
B102
A106

T3 er den eneste loseren — den må undoes. Redo starter ved minste RecLSN i DPT = 101.

Redo-test

For hver log-record (101..108) sjekker vi i denne rekkefølgen:

  1. Hopp over hvis PageID ikke er i DPT.
  2. Hopp over hvis log-record sin LSN < sidens RecLSN.
  3. Les inn siden fra disk. Hopp over hvis sidens PageLSN ≥ log-record sin LSN.
  4. Ellers: redo. Skriv AfterImage til siden. Sett PageLSN = log-record sin LSN.

Anta at A, B, C på disk har PageLSN = 106, 102, 101 ved start av redo:

LSNPageTestResultat
101CPageLSN(C)=101 ≥ 101skip
102BPageLSN(B)=102 ≥ 102skip
106APageLSN(A)=106 ≥ 106skip
107CPageLSN(C)=101 < 107REDO — skriv AfterImage, PageLSN(C):=107

Undo

T3 sin LastLSN er 106. Vi går baklengs (PrevLSN=0, så bare denne ene), skriver en CLR for LSN 106 og angrer endringen på A.

Tenk selv · Eksempel
Hvorfor blir RecLSN for C ikke oppdatert når vi behandler LSN 107 i analysis?
RecLSN er eldste log-record som har gjort siden dirty siden siste flush. Hvis vi oppdaterte RecLSN til 107, ville redo ikke startet tilbake fra 101 — og endringen i LSN 101 kunne mistet sin redo om PageLSN(C) på disk var lavere. Vi må holde på den eldste for å være sikre på korrekt redo.
10 · Sjekkpunkter

Begrens hvor langt tilbake recovery må gå

Uten sjekkpunkter må Analysis lese hele loggen fra start hver gang vi krasjer. Et sjekkpunkt er en periodisk «her var vi» — og recovery kan starte derfra.

Stop-the-world checkpoint (gammelt)

  1. Stopp alle nye operasjoner.
  2. Vent til alle aktive transaksjoner er ferdige (eller i hvert fall stille).
  3. Flush alle dirty sider til disk.
  4. Skriv «checkpoint»-record i loggen.
  5. Slipp alle løs igjen.

Korrekt, men lammer systemet i sekunder — uakseptabelt for OLTP.

Fuzzy checkpoint (ARIES)

  1. Skriv begin_checkpoint-record.
  2. Skriv en end_checkpoint-record som inneholder snapshot av TT og DPT akkurat nå.
  3. Oppdater log anchor (et lite, fast sted på disk) til å peke på begin_checkpoint.
  4. Bakgrunnsprosess flusher dirty sider uten å blokkere drift.

Recovery starter ved begin_checkpoint; den vet hva TT/DPT så ut da, og leser videre forover for å bygge nåværende tilstand.

Trade-off: hyppige sjekkpunkter ⇒ kort recovery, mer normaldrift-overhead. I praksis kjører kommersielle systemer fuzzy checkpoint hvert 5–10 minutt eller hver N-te MB logg.

Sjekk · Sjekkpunkter
Hva er hovedfordelen med fuzzy checkpoint over stop-the-world?
AKortere recovery-tid
BSjekkpunktet kan tas uten å blokkere pågående transaksjoner
CMindre logg-volum
DMindre disk-bruk
Ingen blokkering. Begge typer gir kort recovery — det er hovedhensikten med alle sjekkpunkter. Forskjellen er at fuzzy lar databasen fortsette å betjene transaksjoner mens sjekkpunktet skrives, så systemet aldri stopper synlig for brukeren.
11 · Oppsummering

Kort oppsummert

  • Recovery garanterer Atomicity (undo) og Durability (redo).
  • Winners = committed før krasj. Losers = in-flight ved krasj.
  • ARIES = steal/no-force ⇒ trenger både undo- og redo-logging.
  • WAL-regelen: log-records på disk før dataside. Force-log-at-commit: synkron logg ved commit.
  • CLR gjør undo idempotent. PageLSN på siden følger med.
  • Datastrukturer: Transaction Table (aktive Tx) og Dirty Page Table (sider med endringer i buffer).
  • ARIES tre faser: Analysis → Redo → Undo. Redo «repeating history» — også fra løsere.
  • Fuzzy checkpoint: tar snapshot av TT/DPT uten å blokkere drift.

Du har nå gått gjennom hele kapittel 8. Last igjen oversikten, eller hopp tilbake til alle kapitlene.