Recovery — livet etter krasj
Notater kap. 18. Loggens jobb: garantere atomicity og durability uansett hva som skjer — strømbrudd, OS-kill, deadlock-abort.
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
- Transaksjons-abort under normal drift — manglende data, brukervalg, deadlock.
- System-krasj — strømbrudd, OS-kill, software-feil. RAM forsvinner, disk overlever.
- Mediefeil — disken selv er ødelagt. Trenger backup + redo. (Utenfor pensum.)
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.
Etter krasj må recovery-prosessen:
- Finne ut hvem som var winner og hvem som var loser (Analysis-fasen).
- Sørge for at alle winners' endringer er på disk (Redo-fasen).
- Rulle tilbake alle losers' delvise endringer (Undo-fasen).
commit log-record til log-bufferen i RAM, men maskinen krasjer før loggbufferen blir tømt til disk. Er den winner eller loser?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-steal | Steal | |
|---|---|---|
| Force | Shadowing (ingen logg) | Bare undo-logg |
| No-force | Bare redo-logg | Undo + 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.
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
PageLSNpå 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.
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:
- Undo-WAL — log først, data etterpå (ovenfor). Sikrer atomicity.
- Force-log-at-commit — ved
COMMIT, tving log-bufferen til disk før suksess returneres til klient. Sikrer durability.
Log-buffer
Buffer-pool
Disk
Side C kan ikke skrives til disk før log-record 102 er på disk (FlushedLSN ≥ 102).
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.
Det ARIES holder rede på
To tabeller i RAM (kopieres til logg ved sjekkpunkter):
Transaction Table (TT)
Én rad per aktiv transaksjon. Inneholder:
TransIDStatus— active / committed / abortedLastLSN— 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:
PageIDRecLSN— 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.
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:
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).
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.
Undo
Gå 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.
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.
Analysis i praksis
La A, B, C være data-sider. Etter krasj finner vi følgende logg:
| LSN | PrevLSN | Tx | OpType | Page |
|---|---|---|---|---|
| 101 | 0 | T1 | Update | C |
| 102 | 0 | T2 | Update | B |
| 103 | 101 | T1 | Commit | — |
| 104 | — | — | Begin_ckpt | — |
| 105 | — | — | End_ckpt (TT, DPT) | — |
| 106 | 0 | T3 | Update | A |
| 107 | 102 | T2 | Update | C |
| 108 | 107 | T2 | Commit | — |
Ved sjekkpunkt (LSN 105) hadde TT og DPT følgende:
TT @ ckpt
| Tx | LastLSN | Status |
|---|---|---|
| T1 | 103 | Commit |
| T2 | 102 | Active |
DPT @ ckpt
| Page | RecLSN |
|---|---|
| C | 101 |
| B | 102 |
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
| Tx | LastLSN | Status |
|---|---|---|
| T1 | 103 | Commit |
| T2 | 108 | Commit |
| T3 | 106 | Active → undo! |
DPT etter analysis
| Page | RecLSN |
|---|---|
| C | 101 |
| B | 102 |
| A | 106 |
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:
- Hopp over hvis PageID ikke er i DPT.
- Hopp over hvis log-record sin LSN < sidens RecLSN.
- Les inn siden fra disk. Hopp over hvis sidens PageLSN ≥ log-record sin LSN.
- 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:
| LSN | Page | Test | Resultat |
|---|---|---|---|
| 101 | C | PageLSN(C)=101 ≥ 101 | skip |
| 102 | B | PageLSN(B)=102 ≥ 102 | skip |
| 106 | A | PageLSN(A)=106 ≥ 106 | skip |
| 107 | C | PageLSN(C)=101 < 107 | REDO — 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.
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)
- Stopp alle nye operasjoner.
- Vent til alle aktive transaksjoner er ferdige (eller i hvert fall stille).
- Flush alle dirty sider til disk.
- Skriv «checkpoint»-record i loggen.
- Slipp alle løs igjen.
Korrekt, men lammer systemet i sekunder — uakseptabelt for OLTP.
Fuzzy checkpoint (ARIES)
- Skriv
begin_checkpoint-record. - Skriv en
end_checkpoint-record som inneholder snapshot av TT og DPT akkurat nå. - Oppdater log anchor (et lite, fast sted på disk) til å peke på begin_checkpoint.
- 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.
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.