Verstehe das Sperrmodell von SQLite, seine Transaktionen und die vom SQL-Standard beschriebenen Isolationsanomalien.
Diese Lektion in Kodokon öffnenSQLite sperrt auf der Ebene der gesamten Datenbankdatei, nicht der Zeile. Im Standardmodus (Rollback-Journal) teilen sich Leser eine SHARED-Sperre, aber nur ein Schreiber zur Zeit kann die EXCLUSIVE-Sperre erhalten. Folge: Schreibvorgänge werden serialisiert. Erstellen wir eine Tabelle, um mit Transaktionen zu arbeiten.
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
balance INTEGER NOT NULL
);
INSERT INTO accounts (id, balance)
VALUES (1, 100), (2, 50);
-- Read: deferred transaction (default)
BEGIN;
SELECT balance FROM accounts WHERE id = 1;
COMMIT;Die Sperre entwickelt sich in Stufen: UNLOCKED, dann SHARED (Lesen), RESERVED (Schreibabsicht), PENDING und schließlich EXCLUSIVE (Schreiben). Hält eine Transaktion bereits die Schreibsperre, erhält eine andere den Fehler SQLITE_BUSY. Statt sofort fehlzuschlagen, lege mit busy_timeout eine Wartezeit fest und aktiviere den WAL-Modus, damit Leser und der Schreiber parallel vorankommen.
-- A single writer, concurrent readers
PRAGMA journal_mode = WAL;
-- Wait up to 5 s if the database is busy
PRAGMA busy_timeout = 5000;Der SQL-Standard beschreibt drei Anomalien: den Dirty Read (nicht committete Daten lesen), den Non-Repeatable Read (eine Zeile erneut lesen und einen anderen Wert sehen) und den Phantom-Read (eine Bereichsabfrage erneut ausführen und neue Zeilen auftauchen sehen). Weil SQLite die Schreiber serialisiert, verhält es sich faktisch wie SERIALIZABLE: Diese Anomalien treten nicht auf (außerhalb des Shared-Cache-Modus mit read_uncommitted). Anderswo wählst du die Stufe explizit.
-- Atomic transfer: immediate write lock
BEGIN IMMEDIATE;
UPDATE accounts
SET balance = balance - 10
WHERE id = 1;
UPDATE accounts
SET balance = balance + 10
WHERE id = 2;
COMMIT;BEGIN und BEGIN IMMEDIATE?