MySQL-Replikation leicht gemacht
- Master: neuer User
- Master: my.cnf anpassen
- Slave: my.cnf
- Master: Datensicherung erstellen
- Slave: MySQL in Betrieb nehmen
- Master & Slave: Inbetriebnahme
- Fazit
Die Replikation der Datenbank kann eine feine Sache sein: zwei Datenbanken, idealerweise auf zwei verschiedenen Servern, identischer Datenbestand…
Um es vorwegzunehmen: die Replikation ersetzt kein Backup! Sobald sich Daten auf dem Master ändern – beispielsweise durch ein versehentliches DROP TABLE
– werden die Änderungen auf allen Slaves übernommen; und was weg ist, ist weg, da hilft nur ein Backup :D Geht es allerdings um Redundanz, um Ausfallsicherheit oder Lastverteilung, ist die Replikation eine elegante Wahl. Steigen wir also direkt ein und gehen davon aus, dass der Master-Server im weiteren Verlauf einfach master heißt, der Slave-Server schlicht und ergreifend slave, beide in der Domäne localdomain
.
Master: neuer User
Auf dem Master muss ein neuer MySQL-User angelegt werden, dem im folgenden Schritt die zur Replikation benötigten Rechte zugesprochen werden:
mysql> create user 'replication'@'slave.localdomain' identified by 'einPasswort';
mysql> GRANT REPLICATION SLAVE ON *.* to 'replication'@'slave.localdomain' identified by 'einPasswort';
Dieser Schritt ist für jeden Slave, den man hinzufügen möchte, zu wiederholen; wir gehen nachfolgend aber von einem Setup mit nur einem Slave aus.
Master: my.cnf anpassen
Die Konfiguration des Servers – /etc/mysql/my.cnf
auf Debian-Systemen – muss in Hinblick auf die Replikation bearbeitet werden. Eingefügt werden müssen die nachfolgenden Zeilen – die Parameter sind selbsterklärend, für weitere Erklärungen verweise ich an dieser Stelle auf die READMEs :D
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M
log = /var/log/mysql/mysql.log
Danach einen Restart des mysqld
initiieren.
Slave: my.cnf
Man sollte darauf achten, dass sich Master und Slave hinsichtlich der verwendeten MySQL-Versionen nicht allzusehr unterscheiden (sehr hilfreich ist in diesem Zusammenhang die Webseite Replication Compatibility Between MySQL Versions); im vorliegenden Beispiel wurden identische Systeme verwendet und auf beiden das identische MySQL-Paket installiert. Die Konfiguration des Slaves muss nun der des Masters angepasst werden, insbesondere für den Fall, dass auf dem Master InnoDB verwendet wird. Wichtig: die server-id
muss eine noch nicht verwendete (beliebige, aber positive) ganze Zahl sein; dem Master die 1 zuzusprechen und den Wert für jeden Slave zu inkrementieren ist naheliegend.
server-id = 2
log = /var/log/mysql/mysql.log
Master: Datensicherung erstellen
Auf dem Master erstellen wir nun eine Sicherung der Datenbankinhalte, die wir anschließend auf dem Slave einspielen; in dem Moment haben beide Maschinen einen identischen Datenbestand, auf dem dann zukünftig aufgebaut werden kann. Um die Datensicherung auf dem Master zu erstellen, führen wir im ersten Schritt ein Flush auf die Tabellen aus (d.h. alle Caches werden geleert, also noch ausstehenden Daten werden in Binärdateien geschrieben) und sperren die Tabellen für weitere Schreibzugriffe (da MySQL schon während des Kopierens weitere Daten zwischenspeichern und damit den gewünschten Effekt zunichte machen würde). Das erreichen wir so:
mysql> FLUSH TABLES WITH READ LOCK;
Kein anderer Prozess hat nunmehr Schreibzugriff auf die Tabellen – so lange die Shell, in welcher der Befehl abgesetzt wurde, offenbleibt! Der Schreibschutz wird aufgehoben, wenn entweder die aufrufende Shell geschlossen oder aber das Kommando UNLOCK TABLES
übergeben wird.
Dann schauen wir in der /etc/mysql/my.cnf
, wie datadir definiert ist; im vorliegenden Falle ist das datadir = /data/mysql
, und hier liegen alle Daten, die wir sichern müssen. Also:
$ cd /data/mysql
$ tar cvfj /tmp/mysql_snapshot.tbz .
Die resultierende Datei transportieren wir auf beliebigem Wege zum Slave, sichern sie aber auch für alle Fälle für später (wir würden sie beispielsweise benötigen, wenn wir einen dritten Slave aufsetzen wollen; dies soll aber nicht Bestand dieses Howtos sein). Den Schreibschutz der Tabellen heben wir nun wieder auf.
Hinweis: selbstredend kann das Backup auch über mysqldump
erstellt und via mysql
eingespielt werden; dieses Howto stützt sich jedoch auf die (hoffentlich nicht unbegründete) Annahme, dass vollständiger Shell-Zugriff zu den Systemen möglich ist. Welcher Weg im Endeffekt eingeschlagen wird, liegt im Ermessen des Admins: beide sind gleichwertig.
Slave: MySQL in Betrieb nehmen
Den MySQL-Server auf dem Slave hatten wir bislang nicht gestartet; dennoch überprüfen wir zur Sicherheit, dass er auch wirklich nicht läuft (sollte er es doch tun, muss er gestoppt werden!). Anschließend müssen wir das eben erstellte Backup einspielen; hierzu schauen wir auch auf dem Slave, wie ``datadir in
/etc/mysql/my.cnf definiert ist - hier steht
datadir = /var/lib/mysql. Also legen wir unser
mysql_snapshot.tbz nach
/var/lib/mysql` und packen es dort aus:
$ tar cvfj mysql_snapshot.tbz
Anschließend den Server auf dem Slave starten, die Log-Files prüfen und eventuelle Fehler korrigieren. Läuft nun alles? Prima!
Master & Slave: Inbetriebnahme
Am besten öffnen wir uns nun zwei Shells parallel und loggen uns einerseits auf dem Master, andererseits auf dem Slave ein. Auf dem Master loggen wir uns als User root in die MySQL-Shell ein und ermitteln das aktuelle Logfile:
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000007 | 505273 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
Anschließend loggen wir uns, ebenfalls als User root
, in die MySQL-Shell auf dem Slave ein und machen ihm deutlich, dass er zukünftig seine Daten vom Master beziehen wird. Und beachten: das root
-Passwort für MySQL ist auf dem Slave nun – nach unserem erfolgreichen Einspielen des Backups vom Master – natürlich auch das selbe wie auf dem Master! :D
mysql> CHANGE MASTER TO
-> MASTER_HOST='master.localdomain',
-> MASTER_USER='replication',
-> MASTER_PASSWORD='einPasswort',
-> MASTER_LOG_FILE='mysql-bin.000007';
Den finalen Startschuss geben wir dann auf dem Slave mit folgendem Befehl:
mysql> START SLAVE;
Wir haben auf beiden Maschinen das Logging nach /var/log/mysql/mysql.log
aktiviert; so können wir nun, parallel in beiden Shells, ein tail -f /var/log/mysql/mysql.log
auf beiden Maschinen ausführen und beobachten, was sich tut. Aus Performance-Gründen sollte das Logging für den Produktivbetrieb jedoch wieder deaktiviert werden (Zeile log = /var/log/mysql/mysql.log
auskommentieren, /etc/init.d/mysqld reload
).
Ein show processlist
auf dem Slave kann im laufenden Betrieb dann wie folgt aussehen:
mysql> show processlist;
+------+-------------+-----------+------+---------+-------+-----------------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+-------------+-----------+------+---------+-------+-----------------------------------------------------------------------+------------------+
| 7 | system user | | NULL | Connect | 85194 | Waiting for master to send event | NULL |
| 8 | system user | | NULL | Connect | 3740 | Has read all relay log; waiting for the slave I/O thread to update it | NULL |
| 1147 | root | localhost | NULL | Query | 0 | NULL | show processlist |
+------+-------------+-----------+------+---------+-------+-----------------------------------------------------------------------+------------------+
3 rows in set (0.03 sec)
Und auf dem Master kann das so aussehen (es werden alle momentan verbundenen Slaves angezeigt):
mysql> show processlist;
+------+-------------+---------------------------+--------+-------------+-------+----------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+-------------+---------------------------+--------+-------------+-------+----------------------------------------------------------------+------------------+
| 1554 | replication | slave.localdomain:57823 | NULL | Binlog Dump | 224 | Has sent all binlog to slave; waiting for binlog to be updated | NULL |
| 1557 | root | localhost | NULL | Query | 0 | NULL | show processlist |
+------+-------------+---------------------------+--------+-------------+-------+----------------------------------------------------------------+------------------+
7 rows in set (0.01 sec)
Fazit
Das war die gesamte Magie; der Master führt sozusagen Buch über alle erfolgten Transaktionen, speichert diese im Binlog und gibt sie an alle angeschlossenen Slaves weiter. Die Slaves ihrerseits werten ausschließlich Änderungen an den Datenbanken aus und stehen daher praktisch nicht unter Last. Einsatzszenarien sind nun verschiedene denkbar: beispielsweise könnte der Master die Datenverwaltung an sich und die Weitergabe der Daten an die Slaves verwalten, wohingegen rechenzeitintensive Queries (die den Datenbestand nicht verändern) auf den Slaves ausgeführt werden könnten. Auch lässt sich so ein Switch von datenbankgestützten Anwendungen realisieren… Die Einsatzmöglichkeiten sind vielfältig.
Vielleicht seid ihr ja jetzt auf den Geschmack gekommen; viel Erfolg und viel Spaß :D
Hintergrundbild: 2448x 2448px, Bild genauer anschauen – © Marianne Spiller – Alle Rechte vorbehalten