Hinter der MySQL-Replikation aufräumen

Dieser Artikel ist der dritte Teil einer kleinen Gruppe von Artikeln über MySQL-Backups. Die weiteren Artikel werden hier verlinkt, sobald sie erscheinen.

Hier ist der erste Artikel: MySQL-Backups, aber wie?

Hier ist der zweite Artikel: MySQL Replikation

Hier ist der vierte und letzte Artikel: MySQL mit daemontools

Nachdem ich hier schon vor einer Weile beschrieben habe wie man eine MySQL-Replikation einrichtet und dabei am Rande erwähnt habe, daß man die durch die Replikation anfallenden Binary Logs beizeiten wegrotieren sollte, will ich heute darauf eingehen, wie man das bewerkstelligen kann.

Wer unbedingt möchte, kann sich sein eigenes Skript dazu schreiben, das in allen Slaves nachschaut, welche Binary Log Datei sie gerade lesen und dann auf dem Master alle Binary Log Dateien die älter sind als die älteste von einem Slave genutze entfernt. Diese Vorgehensweise funktioniert, ist aber ein bißchen aufwendiger. Vor allem gibt es ein paar Stolperfallen. So ist es mir zum Beispiel schonmal passiert, daß MySQL sich nach einem Update veranlaßt sah, den üblichen Dateinamen der Binary Logs zu verändern und diese sogar gleich in einem ganz anderen Ordner zu speichern (der Speicherort richtet sich anscheined danach, ob und wenn ja wo PID-Dateien abgelegt werden) — nach solchen Aktionen läuft so ein Skript dann natürlich gegen die Wand. Wobei man dieser Art von Veränderungen vorbeugen kann, indem man in seiner my.cnf explizit einträgt, wo Binary Logs abzulegen sind und wie sie zu heißen haben (mit --log-bin[=base_name]). Wichtig ist bei dieser Vorgehensweise vor allem, daß man keinen Fehler macht und keinem Slave ein Binary Log wegnimmt das er noch benötigt.

Und es gibt noch eine weitere Stolperfalle: Normalerweise heißen Binary Log Dateien z.B. mysql-bin.000008 oder nach dem Hostnamen (es sei denn, man hat mit --log-bin[=base_name] etwas anderes angeordnet). Wer den üblichen Ärger mit Software gewohnt ist, ahnt jetzt schon, was kommt. Die Dateien tragen eine Nummer im Namen die hochgezählt wird — und selbstverständlich auch irgendwann überlaufen wird, später oder eben auch mal früher. Nun könnte man meinen, man hätte ja die alten Binary Logs wegrotiert, da könnte MySQL ja die alten Namen quasi recyceln. Das wird es allerdings nicht tun, denn es pflegt in der Datei mysql-bin.index einen Index der bereits verwendeten Dateinamen (den Namen kann man wieder um mit --log-bin-index[=file_name] festschreiben). Das allerbeste an diesem Index ist, daß man ihn laut der Dokumentation von MySQL nicht editieren sollte während MySQL läuft. Und man sollte ihn editieren, denn wenn sein Inhalt nicht mit dem Zustand auf der Festplatte übereinstimmt, kann das bestimmte MySQL-Versionen verwirren, besonders wenn der unten beschriebene Befehl PURGE BINARY LOGS verwendet wird — neuere Versionen geben dann wenigstens noch eine Fehlermeldung aus.

Es geht aber auch einfacher: MySQL bietet für solche Fälle die Funktion PURGE BINARY LOGS, die hier dokumentiert ist. Sie entfernt nicht nur alte Binary Logs, sie aktualisiert auch den Index.

Hier hat man zwei Möglichkeiten, man kann entweder den Namen der ältesten Datei, die erhalten bleiben soll verwenden und z.B. befehlen PURGE BINARY LOG TO 'mysql-bin.001024'; oder man orientiert sich an einem Datum und befiehlt PURGE BINARY LOG BEFORE '2010-03-26 00:00:00';. Beides hat letztlich den gleichen Effekt, es ist eher eine Geschmacksfrage, welche Variante man nutzen möchte. Die erste hat den Vorteil, daß man das älteste noch benötigte Binary Log relativ bequem von einem Slave abfragen kann. Bei der zweiten Variante müßte man vom Slave abfragen wie viele Sekunden er hinter dem Master zurückliegt und dann den passenden Zeitpunkt berechnen.

Ein Vorteil von PURGE BINARY LOG ist in jedem Fall, daß es keinem aktiven Slave ein Binary Log wegnehmen wird. Sollte man das versuchen oder aus Versehen machen, dann wird MySQL den Fehler abfangen. Moneyquote: „This statement is safe to run while slaves are replicating. You need not stop them. If you have an active slave that currently is reading one of the log files you are trying to delete, this statement does nothing and fails with an error.“ Aber das gilt nur für Slaves, die zu diesem Zeitpunkt eine aktive Verbindung zum Master haben: „However, if a slave is not connected and you happen to purge one of the log files it has yet to read, the slave will be unable to replicate after it reconnects.“

Wer darauf vertraut, daß seine Slaves nicht allzuweit hinterherhinken, kann sogar die Variable expire_log_days in MySQL setzen, dann kümmert sich MySQL automatisch darum, daß die Binary Logs nach der angegebenen Anzahl von Tagen gelöscht werden. Sowas sollte man ggf. mit genügend Abstand einrichten und sich mit Nagios oder etwas ähnlichem einen Check bauen, der einen warnt wenn ein Slave dieser Grenze näher kommt.

Wir verwenden für diese Aufgabe hier ein einfaches Skript, das ich als Referenz hier verlinke.