Datenbanken können aus vielfältigen Gründen beschädigt werden. In meinem Fall war’s vermutlich der Absturz eines Virtualisierers oder ein Stromausfall. Eine der betroffenen virtuellen Maschinen beheimatet eine Zarafa-Installation samt MySQL-Datenbank, die nach dem Neustart der Maschine ihren Dienst verweigerten. Dass Datenbanken es nicht besonders mögen, wenn Sie während eines Schreibprozesses unterbrochen werden, sollte kein Geheimnis sein. Dies kann dazu führen, dass sämtliche Datenbanken auf so einem System beschädigt (corrupt) werden.
Auch sollte es kein Geheimnis sein, dass eine gute Backup-Strategie in so einem Fall Gold wert ist. Nun ist die Welt und vor allem nicht jede EDV-Infrastruktur perfekt. Auf den aktuellen Fall übertragen bedeutet das, dass das letzte Backup ziemlich genau 20 Stunden alt war. Was bei einem E-Mail- und Kommunikations-Server schon eine ganze Menge an Daten bedeuten kann. Also hieß es, andere Wege auszuprobieren, um den Datenverlust möglichst gering zu halten.
Warnung!
Selbst das exakte Befolgen dieser Anleitung garantiert nicht, dass alle Daten gerettet werden können. Daher sollte vor jedwedem Eingriff in MySQL ein umfangreiches Backup erstellt werden. Die folgenden Schritte beschreiben Tätigkeiten, die unwiderrufliche Veränderungen an den Daten vornehmen und somit zu Datenverlust führen können.
MySQL startet nicht: wie stelle ich meine Daten(banken) wieder her?
InnoDB ist eine MySQL-Storage-Engine, welche die Daten für alle Tabellen in den ibdata-Dateien unter /var/lib/mysql speichert. Diese Dateien können aus vielfältigen Gründen beschädigt werden, was fast immer dazu führt, dass MySQL nicht mehr korrekt gestartet wird und die Datenbanken nicht mehr erreichbar sind. In den System-Log-Dateien sah ich haufenweise Meldungen der folgenden Art:
Mar 4 08:47:32 zarafa mysqld_safe: mysqld restarted Mar 4 08:47:34 zarafa mysqld_safe: Number of processes running now: 0 Mar 4 08:47:34 zarafa mysqld_safe: mysqld restarted Mar 4 08:47:35 zarafa mysqld_safe: Number of processes running now: 0
Diese Fehlermeldungen tauchten auf, nachdem ich versucht hatte, den MySQL-Dienst zu starten. Weitere Details ließen sich dann in den MySQL-Log-Dateien (hier: /var/log/mysql/mysql.error) finden. In meinen Fall sah das wie folgt aus:
150304 7:15:31 [Note] Plugin 'FEDERATED' is disabled. InnoDB: Log scan progressed past the checkpoint lsn 4 863699502 150304 7:15:31 InnoDB: Database was not shut down normally! InnoDB: Starting crash recovery. InnoDB: Reading tablespace information from the .ibd files... InnoDB: Restoring possible half-written data pages from the doublewrite InnoDB: buffer... InnoDB: Doing recovery: scanned up to log sequence number 4 863703226 150304 7:15:32 InnoDB: Starting an apply batch of log records to the database... InnoDB: Progress in percents: 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 InnoDB: Apply batch completed 150304 7:15:32 InnoDB: Started; log sequence number 4 863703226 150304 7:15:33 [Note] Event Scheduler: Loaded 0 events 150304 7:15:33 [Note] /usr/sbin/mysqld: ready for connections. Version: '5.1.49-3-log' socket: '/var/run/mysqld/mysqld.sock' port: 3306 (Debian) 150304 7:15:34 InnoDB: Assertion failure in thread 2949921648 in file ../../../storage/innobase/fsp/fsp0fsp.c line 3309 InnoDB: We intentionally generate a memory trap. InnoDB: Submit a detailed bug report to http://bugs.mysql.com. InnoDB: If you get repeated assertion failures or crashes, even InnoDB: immediately after the mysqld startup, there may be InnoDB: corruption in the InnoDB tablespace. Please refer to InnoDB: http://dev.mysql.com/doc/refman/5.1/en/forcing-recovery.html InnoDB: about forcing recovery. 150304 7:15:34 - mysqld got signal 6 ;
Wie rette ich meine Daten?
Um das Wiederherstellen der Daten zu ermöglichen, muss in der MySQL-Konfiguration (/etc/mysql/my.cf) unterhalb von [mysqld] die Zeile „innodb_force_recovery=4“ eingefügt werden. Hierzu sollte der MySQL-Dienst vorher angehalten werden.
Weitere Details zu dieser Option finden sich in der MySQL-Dokumentation (http://dev.mysql.com/doc/refman/5.1/en/forcing-innodb-recovery.html).
Nach einem Neustart des MySQL-Servers können wir versuchen, die Daten der defekten Datenbank mittels mysqldump zu exportieren.
Für eine einzelne Datenbank:
mysqldump datenbank_name > database_name.sql
Für alle Datenbanken :
mysqldump -A > all_your_bases.sql
Nachdem alle defekten Datenbanken erfolgreich exportiert wurden, muss im nächsten Schritt MySQL komplett angehalten werden. Anschließend müssen die InnoDB-Dateien /var/lib/mysql/ib* gelöscht werden (vorsichtshalber sollte ein Backup dieser Daten vorher an anderer Stelle abgelegt werden):
mkdir /var/lib/old_innodb_data mv /var/lib/mysql/ib* /var/lib/old_innodb_data
Nachdem die ib*-Dateien aus /var/lib/mysql/ verschoben wurden muss die Option „innodb_force_recovery“ ebenfalls wieder aus der MySQL-Kofiguration entfernt werden. Nach einem Neustart des MySQL-Server sollten entsprechende Meldungen in den Log-Dateien darauf hindeuten, dass die ib*-Dateien neu angelegt werden. Sobald MySQL ohne Fehler startet, kann mit der Wiederherstellung der defekten Datenbanken begonnen werden.
Wurden nur einzelne Datenbanken exportiert, dann werden diese wie folgt wieder eingespielt:
- Datenbank ‚droppen‘ (in mysql: „drop database datenbank_name;“)
- Datenbank anlegen (in mysql: „create database datenbank_name;“)
- Einspielen des SQL-Dumps von der Konsole: „mysql datenbank_name < datenbank_name.sql“
Wurden alle Datenbanken exportiert, sollte folgender Aufruf ausreichen:
mysql < all_your_bases.sql
Nachdem das Wiederherstellen erfolgreich durchgeführt wurde (je nach Umfang der Datenbank(en) kann das eine Weile dauern), sollte man per mysqlcheck überprüfen, ob alle Datenbanken wieder intakt sind:
mysqlcheck –all-databases –repair
Zum Schluss rate ich einen Neustart des MySQL-Servers unter Beobachtung der Log-Dateien an, aber im Normalfall sollte jetzt wieder alles funktionieren.
Hier noch mal eine kurze Schritt-für-Schritt-Anleitung:
- MySQL-Dienst stoppen
- Backup von InnoDB-Dateien anlegen (/var/lib/mysql/ib*)
- In der MySQL-Konfiguration (/etc/mysql/my.cnf) unterhalb von [mysqld] die Zeile „innodb_force_recovery=4“ einfügen
- MySQL-Dienst starten
- Entweder alle Datenbanken (mysqldump -A > dump.sql) oder gezielt einzelne Datenbanken (mysqldump zarafa > dump.sql) exportieren
- Alle exportierten Datenbanken ‚droppen‘
- MySQL-Dienst anhalten
- Die InnoDB-Dateien /var/lib/mysql/ib* löschen
- Die in Punkt 3. eingefügte Zeile „innodb_force_recovery=4“ aus der MySQL-Konfiguration löschen
- MySQL-Dienst starten. In den MySQL-Log-Dateien sollten nun Meldungen auftauchen, dass die gerade gelöschten ib*-Dateien neu angelegt werden.
- Die Datenbanken mit Hilfe des dumps wiederherstellen (mysql < dump.sql)
Anmerkungen
- Ein höherer Wert als 4 bei der Option „innodb_force_recovery“ führt fast immer zu inkonsisten Daten und entsprechendem Datenverlust und sollte daher vermieden werden. Bereits ein Wert von „4“ kann zu einem Verlust von Daten führen.
- Mit dem folgenden SQL-Befehl können alle Tabellen gefunden werden, die die innoDB Storage-Engine nutzen:
SELECT table_schema, table_name FROM INFORMATION_SCHEMA.TABLES WHERE engine = 'innodb';
Man kann Datenbankausfälle zwar nie ganz ausschließen – aber zu wissen, wie man die dadurch verursachten Schäden möglichst klein hält, kann einem Kunden den Tag retten. Trotzdem bleiben eine gute Backup-Strategie und möglichst ausfallsichere Hardware bei unternehmenskritischen Systemen unersetzlich.