mysql Replikation

wall < "MySQL-Replikation leicht gemacht"

Broadcast message from spillerm@unixe.de (pts/1) (Sa Apr 11 12:07:05 2009):
4
Diesen Beitrag schrieb ich vor 10 Jahren. Behalte das beim Lesen bitte im Hinterkopf.

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 heisst, 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 im 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 zuzusprechend 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 anschliessend 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 — solange 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!). Anschliessend 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

Anschliessend 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)

Anschliessend 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)

Abschliessend

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 ausschliesslich Ä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 Spass :D

4
  1. Danke für die immer noch gut zu gebrauchende Anleitung. Sie hat mir immer wieder das Leben gerettet, wenn meine Replikation zusammengebrochen ist

  2. Hallo zusammen,

    eine Frage zur Replikation. Ich habe einen funktionierenden Master- und Slave(Replikations)server. Die Replikation funktioniert auch wunderbar. Eingerichtet ist das Ganze für den Fall der Fälle, dass der Master mal die Hufe hochreist.
    Ist es jetzt möglich, schnell aus dem Slave einen Masterserver zu konfigurieren (MySQL-Version 4.0.18 wird noch verwendet)?
    Vielen Dank schon mal für Eure Hilfe.

  3. Christian Kostenzer

    Hi – feines tutorial, herzlichen Dank dafür.
    Eine kleine Anregung:
    Die Salve Konfiguration würde ich noch um eine Zeile erweitern, nämlich:

    MASTER_LOG_POS=xxxx;

    xxxx bekommt man vom Master Status geliefert.
    Da mit dem Dump die Daten ja synchron sind, kann auf dem aktuellen Stand beginnen, andernfalls der Master beginnen würde alles aus dem angegebenen Logifle zu synchronisieren. Bei vielen bzw. grossen DBs, kann das dann schon mal dauern bis er durch ist …

    Grüsse aus den Alpen

  4. Super Howto. Hat mit aktuellen Debian Squeezy super funktioniert :)

  5. Ich frage mich, ob es beim ersten Teil beim Kopieren der Daten vom Masterserver auf den Slave (mit tar) auch möglich ist, nur z.B. EINE Datenbank statt des kompletten datadir zu kopieren? Im meinem Szenario darf das auf keinen Fall passieren. Denn auf dem Slave laufen schon etliche andere Datenbanken, die auch nicht von der Replikation betroffen sein sollen.

  6. Hi Ho!

    Das How-To ist echt Top!!!

    Hat mir sehr geholfen, funktioniert richtig gut und ist dabei noch recht Simpel.

    Ein Hinweis aber noch (ich nutze Debian und Ubuntu):
    in der Datei /etc/mysql/debian.cnf muss das Passwort und der User auf dem Slave und Master gleich sein, es reicht nicht (bei mir zumindest), das Data-Dir vom Master auf den Slave zu kopieren ^^

    LG
    Luke

  7. Vielen herzlichen Dank für dieses vorzügliche HowTo!

  8. Danke für dieses brauchbare HowTo… gleich mal an 100 Leute rum geschickt ;-)

Keine weitere Reaktionen mehr möglich.