Layer 8Ehrlicher Tech-Support

SQLite: Nach dem Kopieren fehlen die letzten Änderungen

Die .db-Datei allein ist im WAL-Modus keine vollständige Datenbank. Ohne die -wal-Datei fehlen Transaktionen oder die Kopie ist beschädigt.

Aufwand:
ca. 20 Min.
Voraussetzung:
Kommandozeilenwerkzeug sqlite3, Schreibrechte im Datenbankverzeichnis
Gilt für:
SQLite 3

Das Symptom

Die Anwendung wurde beendet, die Datenbankdatei auf einen anderen Rechner kopiert, dort geöffnet — und der Datenstand ist von vorgestern. Die Einträge der letzten Tage fehlen vollständig, obwohl sie vor dem Kopieren sichtbar waren. Im ungünstigen Fall meldet SQLite die Datei stattdessen als beschädigt.

Vorher lagen im Verzeichnis drei Dateien: daten.db, daten.db-wal und daten.db-shm. Kopiert wurde eine. Manchmal ging dem Ganzen auch ein Aufräumen voraus, bei dem die beiden vermeintlichen Hilfsdateien gelöscht wurden, weil die -wal-Datei größer war als die Datenbank selbst.

Die „Layer 8“-Wahrheit

Im WAL-Modus ist die .db-Datei nicht die Datenbank, sondern nur ihr größerer Teil. Neue Transaktionen werden zuerst in das Write-Ahead-Log geschrieben und erst bei einem Checkpoint in die Hauptdatei übertragen. Zwischen diesen beiden Zeitpunkten liegen die jüngsten Daten ausschließlich in der -wal-Datei. Wer nur die .db-Datei mitnimmt, nimmt eine ältere Fassung mit und hält sie für die aktuelle.

Die Dokumentation formuliert die Folge ohne Beschönigung: Wird eine Datenbankdatei von ihrer WAL-Datei getrennt, können zuvor bestätigte Transaktionen verloren gehen oder die Datenbankdatei kann beschädigt werden. Das ist keine Warnung vor einem Randfall, sondern die Beschreibung des Normalfalls. Und es gibt genau einen dokumentierten Weg, das Log loszuwerden: die Datenbank ordentlich öffnen und wieder schließen. Dabei räumt SQLite selbst auf. Der Dateimanager kennt diesen Weg nicht.

Das Ärgerliche daran ist, wie unauffällig der Schaden ist. Es gibt keine Fehlermeldung, keinen roten Kasten, keinen Absturz. Die kopierte Datenbank öffnet sich anstandslos und liefert bereitwillig einen Datenstand — nur eben den falschen. Auffallen tut das erst, wenn jemand einen Eintrag sucht, den er selbst angelegt hat. Bis dahin läuft alles bestens.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Feststellen, ob die Datenbank überhaupt im WAL-Modus läuft

    Erst klären, ob es das Problem sein kann. Der Befehl gibt den Journalmodus zurück; laut Dokumentation sind DELETE, TRUNCATE, PERSIST, MEMORY, WAL und OFF möglich, Vorgabe ist DELETE. Steht dort wal, gehören -wal und -shm zur Datenbank dazu. Der Aufruf öffnet und schließt die Datei nebenbei sauber — genau das, was SQLite als sicheres Vorgehen beschreibt.

    sqlite3 daten.db "PRAGMA journal_mode;"
    
  2. 2.

    Das Log in die Datenbankdatei zurückschreiben

    Vor jedem Kopieren, Verschieben oder Sichern das Write-Ahead-Log einsammeln. Die Dokumentation beschreibt für den Checkpoint vier Betriebsarten; TRUNCATE arbeitet wie RESTART und kürzt die WAL-Datei nach erfolgreichem Abschluss zusätzlich auf null Bytes. Danach steht alles Bestätigte in der .db-Datei — und nur danach ist sie allein aussagekräftig.

    sqlite3 daten.db "PRAGMA wal_checkpoint(TRUNCATE);"
    
  3. 3.

    Beim Kopieren alle drei Dateien mitnehmen

    Wenn das Programm weiterläuft und ein Checkpoint nicht möglich ist, gilt: entweder alle zusammengehörenden Dateien kopieren oder gar keine. Die Trennung von Datenbank und Log ist genau der Vorgang, bei dem laut Dokumentation Transaktionen verloren gehen oder die Datei beschädigt werden kann. Ein Sicherungsskript, das nur *.db einsammelt, ist keine Sicherung, sondern eine Verlosung.

    cp daten.db daten.db-wal daten.db-shm /pfad/zur/sicherung/
    
  4. 4.

    Besser: eine konsistente Kopie von SQLite selbst anfertigen lassen

    Statt Dateien im laufenden Betrieb einzusammeln, die Datenbank die Kopie schreiben lassen. Der eingebaute Sicherungsbefehl des Kommandozeilenwerkzeugs erzeugt eine in sich schlüssige Datei, ohne dass man über Journalstände nachdenken muss. Das ist der Unterschied zwischen einer Sicherung und drei Dateien, die zufällig zur selben Sekunde gelesen wurden.

    sqlite3 daten.db ".backup sicherung.db"
    
  5. 5.

    Nach einem Unfall prüfen, was noch heil ist

    Wurde die -wal-Datei bereits gelöscht, hilft nur noch eine Bestandsaufnahme. Die Prüfung sucht laut Dokumentation unter anderem nach Tabellen- und Indexeinträgen in falscher Reihenfolge, fehlerhaft aufgebauten Datensätzen, fehlenden Seiten sowie verletzten UNIQUE-, CHECK- und NOT-NULL-Bedingungen. Kommt ok zurück, ist die Datei in sich schlüssig — die Änderungen aus dem gelöschten Log bringt das trotzdem nicht zurück.

    sqlite3 daten.db "PRAGMA integrity_check;"
    

Häufige Fragen (FAQ)

?Warum liegen neben meiner Datenbank noch eine -wal- und eine -shm-Datei?

Weil die Datenbank im WAL-Modus läuft. Die SQLite-Dokumentation beschreibt es so: SQLite führt eine zusätzliche Journaldatei, das Write-Ahead-Log, deren Name üblicherweise der Name der Datenbankdatei mit dem Zusatz -wal ist. Die -shm-Datei ist der zugehörige Index im gemeinsamen Speicher, über den Leser die Seiten im Log finden. Neu geschriebene Daten landen zuerst im Log und wandern erst bei einem sogenannten Checkpoint in die eigentliche Datenbankdatei. Solange das nicht passiert ist, steht ein Teil der Wahrheit in der -wal-Datei und nicht in der .db-Datei.

?Kann ich die -wal-Datei einfach löschen, wenn sie mir zu groß wird?

Nur auf einem einzigen dokumentierten Weg, und der heißt nicht rm. Die Dokumentation ist an dieser Stelle unmissverständlich: Wird eine Datenbankdatei von ihrer WAL-Datei getrennt, können zuvor bestätigte Transaktionen verloren gehen oder die Datenbankdatei kann beschädigt werden. Als einzig sicheres Vorgehen nennt SQLite, die Datenbank über eine der sqlite3_open-Schnittstellen zu öffnen und sofort wieder mit sqlite3_close zu schließen. Auf der Kommandozeile heißt das schlicht: einmal sqlite3 auf die Datei loslassen und wieder beenden.

?Ich habe die Datenbank auf DELETE zurückgestellt, trotzdem ist sie beim nächsten Start wieder im WAL-Modus. Warum?

Weil der Modus keine Einstellung der Verbindung ist, sondern eine Eigenschaft der Datei. Die Dokumentation stellt fest, dass PRAGMA journal_mode=WAL im Gegensatz zu den anderen Journalmodi dauerhaft ist: Setzt ein Prozess den WAL-Modus, dann schließt und öffnet die Datenbank erneut, kommt sie im WAL-Modus zurück. Wer das ändern will, muss den Modus explizit auf einen anderen Wert setzen und dabei alleiniger Nutzer der Datei sein — nicht darauf hoffen, dass sich das beim Neustart von selbst erledigt.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWLästigdatabase is locked

SQLite: database is locked

SQLite meldet „database is locked“? Sie erlaubt nur einen Schreiber — ein hängender Zugriff oder ein Netzlaufwerk bringt sie ins Stocken.

SWLästigAccess denied for user

MySQL: Access denied for user

Der Login wird abgewiesen, obwohl das Passwort stimmt? Bei MySQL zählt auch, von welchem Host aus man kommt — der Benutzer ist an eine Herkunft gebunden.