Wenn es kracht – Wiederherstellen einer MySQL Datenbank aus Dumps und Binary Logs

Wie im vorherigen Artikel dargestellt lässt sich eine MySQL Datenbank mit regelmäßigen Dumps und aktivierten Binary Logs fortlaufend sichern. In diesem Artikel wird kurz dargestellt, wie sich eine Datenbank aus einem Dump und den Binary Logs wiederherstellen lässt.

Meist ist der Fehlerfall harmloser als die Armageddon-Szenarien, die immer prophezeit werden. Sollte das System also das zeitlich gesegnet haben, wird das Ersatzsystem mit dem Dump und den Binary Logs wieder aufgesetzt. Etwas komplizierter – und deshalb hier beschrieben – ist das teilweise Wiederherstellen aus den Binary Logs, wenn die Datenbank aufgrund fehlerhafter Queries gelöscht oder fehlerhaft verändert wurde. Die Beispiele sind auf einem Debian/GNU Linux 7.x System mit MySQL 5.5 Community Edition entstanden.

> Bitte keine Kommandos per Copy 'n Paste auf Produktivsystemen ausführen, ohne zu verstehen, was diese auslösen. < 
Dies sind Beispiele, die an die eigenen Bedürfnisse angepasst werden müssen.

Angenommen ein beherztes 'DROP DATABASE example_db' hat wichtige Kundendaten gelöscht. Als Erstes erstellen wir die Datenbank neu und spielen den Dump ein:

mysql -p y -u x
mysql> CREATE DATABASE example_db;
mysql> USE example_db;
mysql> SOURCE /var/log/mysql/dump.sql
...

Das wird je nach Datenbankgröße einige Zeit dauern, sodass wir parallel in einer weiteren Shell die Binary Logs vorbereiten können. Dazu ermitteln wir alle Binary Logs die nach dem Dump erstellt wurden. Dies kann an den Dateizeiten der Binary Logs oder im Kopf des Dump ausgelesen werden:
...
--
-- Position to start replication or point-in-time recovery from
--
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000287',
MASTER_LOG_POS=120;
...

In diesem Beispiel werden die Binary Logs ab mysql-bin.000287 für die Wiederherstellung interessant. Die notwendigen Binary Logs werden nun in einfache SQL Dateien umgewandelt. In diesem Fall betrifft das die Dateien mysql-bin.000287 bis mysql- bin.000289

mysqlbinlog -r mysql-bin.000287.sql mysql-bin.000287
mysqlbinlog -r mysql-bin.000288.sql mysql-bin.000288
mysqlbinlog -r mysql-bin.000289.sql mysql-bin.000289

Nun muss die Log-Position ermittelt werden an der das unerwünschte 'DROP DATABASE' zu finden ist. Die Position wird vor jedem Query in der Form '# at 123456' vermerkt. Zur Wiederherstellung ist die Position des letzten gewünschten Query notwendig. Diese kann manuell in der Datei gesucht werden und wird für den gegebene Fall mit folgendem Kommando ermittelt:

grep -e "DROP DATABASE" -e "# at " mysql-bin.000289.sql |grep -B2 "DROP DATABASE"

Ausgabe:

# at 86881515
# at 86881546
DROP DATABASE example_db

Mit der gefunden Position 86881515 wird nun die SQL-Datei für den Binary Log mysql- bin.000289 auf die passende Länge gekürzt erstellt.

mysqlbinlog -r mysql-bin.000289.sql –stop-position=86881515 mysql- bin.000289

Dabei bestimmt der Parameter –stop-position den letzten auszulesenden Query. Somit können nun, nachdem der parallel eingespielte Dump abgeschlossen ist, auch die Inkremente eingespielt werden:
...
mysql> SOURCE /var/log/mysql/mysql-bin.000287.sql
...
 ...
mysql> SOURCE /var/log/mysql/mysql-bin.000288.sql
...
 ...
mysql> SOURCE /var/log/mysql/mysql-bin.000289.sql
...

Die Datenbank ist nun wieder auf dem Stand vor dem DROP-Query und kann wieder in Betrieb genommen werden.