Am 27. November habe ich in einer Testdatenbank ein DELETE FROM bestellungen ohne WHERE ausgeführt. Es war 11:22 Uhr, die Datenbank war eine Kopie, und es war Absicht. Ich wollte wissen, was passiert, wenn es keine Absicht gewesen wäre – und ob ich den Zustand von vor fünfzehn Minuten wiederherstellen kann, ohne den ganzen Tag zu verlieren.
Die Antwort war: ja, aber nicht in fünfzehn Minuten. Der Restore hat 22 Minuten gedauert, und das ist die gute Nachricht. Die schlechte Nachricht ist der sechste Versuch, bei dem ich eine Stunde gebraucht habe, weil ein WAL-Segment fehlte.
Was WAL-G macht
Zwei Dinge, die man auseinanderhalten muss. Erstens das Basisbackup: ein konsistenter Schnappschuss der Datenfiles, in meinem Fall einmal pro Tag um 02:00 Uhr, komprimiert in einen S3-kompatiblen Speicher. Zweitens die WAL-Archivierung: Jede Änderung, die Postgres schreibt, wird als Segment hochgeladen, sobald es voll ist oder spätestens nach 60 Sekunden.
Point-in-Time-Recovery setzt beides zusammen: Du spielst das Basisbackup ein und lässt Postgres die WAL-Segmente bis zu einem Zeitpunkt abspielen. Der Zeitpunkt ist ein Zeitstempel oder eine LSN, und genau dort liegt die erste Falle. „15 Minuten zurück“ ist keine Angabe, mit der ein Werkzeug arbeitet – es braucht ein recovery_target_time, und das muss in UTC sein, sonst landest du eine oder zwei Stunden daneben.
Sechs Restores, sechs Zeiten

Ich habe sechs Ziele gewählt: 15 Minuten, eine Stunde, sechs Stunden, ein Tag, sieben Tage zurück und einen Zeitpunkt kurz vor dem Löschen. Jeder Restore in eine frische Instanz, gleiche Hardware, gleiche Netzwerkstrecke. Gemessen habe ich von „Befehl abgeschickt“ bis „Datenbank nimmt Verbindungen an und die Zeilenzahl stimmt“.
| Versuch | Ziel (zurück) | Dauer | Ergebnis |
|---|---|---|---|
| 1 | 15 Minuten | 22 min | ok, 1.284 fehlende Zeilen erwartet |
| 2 | 1 Stunde | 26 min | ok |
| 3 | 6 Stunden | 31 min | ok |
| 4 | 1 Tag | 38 min | ok |
| 5 | 7 Tage | 47 min | ok, aber Basisbackup von vor 8 Tagen |
| 6 | vor dem DELETE | 61 min | erst nach manuellem Eingriff |
Versuch 6 ist der lehrreiche. Postgres brach mit „could not find WAL segment“ ab, und zwar an einer Stelle, die im Log wie ein Datenproblem aussah, aber keines war: Beim Failover im Oktober hatte der neue Primary eine neue Timeline begonnen, und die WAL-Kette hatte dort eine Lücke. Die Archivierung lief weiter, aber ein Segment fehlte dauerhaft. Ohne diesen Test hätte ich das erst im Ernstfall gemerkt.
Wie ich die Zeiten gemessen habe
Ich habe jeden Restore mit Zeitstempeln in eine CSV geschrieben, weil ich die Zahlen nicht aus dem Kopf in einen Blogpost übertragen wollte. Der Unterschied zwischen „Restore fertig“ und „Datenbank nutzbar“ ist übrigens real: Nach dem letzten WAL-Segment braucht Postgres noch Zeit für den Consistency-Check, und in meinem Fall waren das vier bis neun Minuten.
import pandas as pd
runs = pd.read_csv("restores.csv")
# spalten: versuch, ziel_minuten, start_utc, pg_ready_utc, zeilen_ok
runs["start"] = pd.to_datetime(runs.start_utc, utc=True)
runs["ready"] = pd.to_datetime(runs.pg_ready_utc, utc=True)
runs["rto_min"] = (runs.ready - runs.start).dt.total_seconds() / 60
print(runs[["versuch", "ziel_minuten", "rto_min", "zeilen_ok"]])
print("Median RTO:", round(runs.rto_min.median(), 1), "min")
print("Maximum: ", round(runs.rto_min.max(), 1), "min")
# Wie viel Zeit kostet das Abspielen pro Stunde Rückwärtsgang?
runs["pro_stunde"] = runs.rto_min / (runs.ziel_minuten / 60)
print(runs[["ziel_minuten", "rto_min", "pro_stunde"]].round(2))
Die letzte Spalte war die interessanteste: Der Restore wird nicht linear teurer. Von 15 Minuten auf eine Stunde kostet vier Minuten mehr, von einem Tag auf sieben Tage neunzehn. Der Grund ist, dass das Basisbackup den größten Block ausmacht und die WAL-Segmente danach relativ günstig sind. Für die Planung heißt das: Die Zielzeit ist fast egal, die Backup-Größe nicht.
Was ich nicht getestet habe
Ich habe nicht getestet, was passiert, wenn der Speicher langsam ist. Meine Messung lief gegen ein lokales MinIO im selben Netz, und in der Produktion liegt der Bucket woanders. Ich habe auch keine parallelen Restores getestet, und ich habe die Wiederherstellung in die bestehende Instanz nicht angefasst – nur in frische Instanzen. Das sind drei Lücken, die ich kenne und die in einem echten Notfall relevant werden.
Was ich getestet habe, ist die Frage, die zählt: Wie lange dauert es, bis jemand wieder arbeiten kann. Die Antwort ist eine Zahl mit einer Verteilung, nicht ein Gefühl von „das läuft schon“. Und sie hängt an einer Kleinigkeit, die niemand aufschreibt: ob die WAL-Kette lückenlos ist.
Fazit
Ein Backup ohne Restore-Test ist eine Datei mit Hoffnung. Bei mir hat der Test einen Fehler gefunden, der seit zwei Monaten im Archiv lag, und er hat eine Zahl geliefert, mit der ich argumentieren kann: 22 Minuten für 15 Minuten zurück, 47 Minuten für sieben Tage. Diese Zahlen gehören in den Betriebshandbuch, nicht in ein Wiki, das niemand liest.
Meine Frage an dich: Wann hast du zuletzt einen Restore gefahren, ohne dass es ein Notfall war – und weißt du, ob deine WAL-Kette an der Stelle des letzten Failovers noch vollständig ist?
yaraosman
ein delete ohne where als absicht zu bezeichnen ist mutig, ich hätte dabei gezittert
marwankhalil
22 minuten für fünfzehn minuten zurück, wo war der unterschied, im segment oder im netz?
oemeryildiz
der sechste versuch mit dem fehlenden segment ist der teil, den ich in jeder doku vermisse
felixwagner
ich würde so einen test nie in einer kopie machen, weil die kopie nie die größe der echten datenbank hat
elifdemir
im segment, der restore hat die reihenfolge nicht gefunden und dann neu angefangen, das waren allein neun minuten
elifdemir
die kopie hatte 40 prozent der größe, und der restore war trotzdem doppelt so lang wie geplant