🔒Journal, WAL und Sperren

SQLite ist ACID: Eine Transaktion wird ganz oder gar nicht wirksam – auch bei Absturz oder Stromausfall mitten im Schreiben. Dafür gibt es zwei Verfahren: das klassische Rollback-Journal und seit Version 3.7.0 (2010) das Write-Ahead-Log. Quellen: sqlite.org/atomiccommit.html, /lockingv3.html, /wal.html.

🪜Sperrzustände (Rollback-Journal-Modus)

Jede Verbindung durchläuft diese Stufen. Die Sperren sind Byte-Bereichs-Sperren auf der Datenbankdatei (unter Unix fcntl) – genau dafür gibt es die unbenutzte Lock-Byte-Seite bei 1 GiB.
UNLOCKED

Keine Sperre. Die Datei darf weder gelesen noch geschrieben werden.

SHARED

Lesen erlaubt. Beliebig viele Verbindungen gleichzeitig.

RESERVED

Schreibabsicht. Nur eine Verbindung; Leser dürfen weiter kommen und gehen. Änderungen liegen erst im Cache + Journal.

PENDING

Warten auf Leser. Keine neuen SHARED-Sperren mehr – verhindert, dass Schreiber „verhungern“.

EXCLUSIVE

Schreiben in die Datenbankdatei. Kein anderer darf irgendeine Sperre halten.

📓Rollback-Journal als Zeitleiste

Zwei Leser, ein Schreiber, Seiten 1–3. Verfolge Sperren, Journal und Datenbankdatei – und schalte auf „Absturz“, um das heiße Journal in Aktion zu sehen.
Schritt 1 / 12 · Tasten ← →
System

Ausgangslage

Niemand hat die Datei geöffnet. Seiten 1–3 haben den Stand v1.

Leser AUNLOCKED
Leser BUNLOCKED
SchreiberUNLOCKED
🧠 Cache des Schreibers
leer
📓 db.sqlite-journal
existiert nicht
💾 db.sqlite
S. 1v1
S. 2v1
S. 3v1

📜Write-Ahead-Log als Zeitleiste

Im WAL-Modus bleibt die Datenbankdatei beim Commit unberührt. Änderungen werden als Frames angehängt; Leser merken sich, bis zu welchem Frame sie lesen (mxFrame). Ein Checkpoint schreibt die Frames später zurück.
Schritt 1 / 11 · Tasten ← →
System

Ausgangslage

journal_mode=WAL. Datenbankdatei mit Seiten 1–3 in Version v1, das WAL ist leer.

💾 db.sqlite
S. 1v1
S. 2v1
S. 3v1
📜 db.sqlite-wal · Salz 1
leer

✔ = Commit-Frame · grün = schon zurückgeschrieben · blass = noch nicht committet

🗂️ db.sqlite-shm (wal-index)
mxFrame = 0 · nBackfill = 0
Seite → Frames: –

🧪WAL-Labor

Selbst ausprobieren: Leser starten, Seiten schreiben, committen, Checkpoints auslösen. Die Frame-Köpfe darunter werden mit dem echten Prüfsummenverfahren berechnet (getestet gegen eine von SQLite erzeugte -wal-Datei).
Leser A
Leser B
Schreiber
Checkpoint
journal_mode=WAL – leeres WAL, Seiten 1–3 in v1.
💾 db.sqlite
S. 1v1
S. 2v1
S. 3v1
📜 db.sqlite-wal · Salz 1
leer

✔ = Commit-Frame · grün = schon zurückgeschrieben · blass = noch nicht committet

🗂️ db.sqlite-shm (wal-index)
mxFrame = 0 · nBackfill = 0
Seite → Frames: –
WAL-Datei im Hex (berechnet: echter Kopf, echte Frame-Köpfe mit Prüfsummen; Seiteninhalt nur als Muster)
Kopf 377f0682002de2180000040000000000000000015eed0001d5cf03138eabc816

Kopf: Magic 0x377f0682 · Version 3007000 · Seitengröße · Checkpoint-Nr. · Salz 1 + 2 · Prüfsumme. Frame: Seitennummer · DB-Größe nach Commit (≠ 0 nur beim Commit-Frame) · Salze · laufende Prüfsumme.

⚖️Journal oder WAL?

Rollback-Journal (DELETE, TRUNCATE, PERSIST)WAL
Wohin schreibt ein Commit?direkt in die Datenbankdatei (vorher Originale ins Journal)ans Ende der -wal-Datei
Leser während des Schreibensbis PENDING ja, dann blockiertimmer – jeder Leser hat seinen Schnappschuss
Gleichzeitige Schreibereinereiner
fsyncs je Commit (synchronous=FULL)mehrere (Journal, Datenbank, Löschen)einer auf das WAL (bei NORMAL keiner – erst beim Checkpoint)
Zusatzdateien-journal (nur während Transaktionen)-wal und -shm (dauerhaft, solange geöffnet)
Netzlaufwerkmöglich, wenn Dateisperren zuverlässig sindnein – der wal-index braucht gemeinsamen Speicher auf einem Rechner
Große TransaktionengutWAL wächst entsprechend; Checkpoint danach
💡 Automatischer Checkpoint
Standardmäßig checkpointet SQLite, sobald das WAL 1000 Seiten erreicht (PRAGMA wal_autocheckpoint), und beim Schließen der letzten Verbindung.
✅ Salz und Prüfsumme
Beim Neustart des WAL ändern sich die Salze. Frames mit altem Salz oder falscher Prüfsumme gelten als ungültig – so erkennt SQLite halb geschriebene Frames nach einem Absturz.
⚠️ -wal nicht vergessen
Wer eine Datenbank im WAL-Modus kopiert, muss -wal mitnehmen oder vorher checkpointen – sonst fehlen die letzten Transaktionen. Besser: VACUUM INTO oder die Backup-API.