Seitenanfang

Der erste mySQL-Slave

Dieser Post wurde aus meiner alten WordPress-Installation importiert. Sollte es Darstellungsprobleme, falsche Links oder fehlende Bilder geben, bitte einfach hier einen Kommentar hinterlassen. Danke.


Heute wurde mir eine kurze Frage gestellt:
Wir wollen in Richtung DB Slaves gehen, um die Hauptdatenbank zu entlasten. Ich würde gerne aus Deiner Sicht erfahren, wo Probleme auftauchen können.
Ob der Fragesteller wusste, was er für eine Antwort provoziert hat?

Eine mySQL-Replikation (und möglicherweise auch die Replikationen anderer SQL-Datenbanken, mit denen habe ich allerdings keine Erfahrung) hat zwei grundsätzliche Eigenschafen, die man akzeptieren muss, alles andere führt zu Problemen und falschen bzw. alten Daten:

1. Ein mySQL-Slave ist niemals auf dem verlässlich gleichen Stand wie sein Master!

Aber es gibt doch "SHOW SLAVE STATUS", die "seconds_behind_master" und die "MASTER_POS_WAIT"-Funktion - ja, beide gibt es.

"SHOW SLAVE STATUS" locked (zumindest in unserer mySQL-Version) und kann leicht mehrere Sekunden oder sogar Minuten brauchen, um eine Antwort zu liefern. Allerdings ist es für die Statusabfrage leider unerlässlich.

"seconds_behind_master" gibt zwar theoretisch an, ob der Slave aktuell ist ("0 seconds behind") oder wie viel älter die Daten im Vergleich zum Master sind, allerdings wirklich nur theoretisch. Tatsächlich liefert der Master dern Timestamp jedes replizierten Queries mit und der seconds-behind-master - Wert gibt nur an, wie viel Zeit zwischen der Ausführung des Querys auf dem Master und auf dem Slave vergangen ist. Jetzt hoffen wir alle mal, dass die Replikation wirklich sequenziell erfolgt und sich nicht ein "altes" Query dazwischen schieben kann (verlassen würde ich mich allerdings nicht darauf).

Beim Wechsel zwischen zwei BinLog-Dateien können hier sehr komische Werte auftreten, bis hin zum NULL (bzw. undef in Perl), welches normalerweise auf eine unterbrochen Replikation oder einen nicht nutzbaren Slave hindeutet.

Es gibt zwar noch die MASTER_POS_WAIT-Funktion, mit der auf einen spezifischen Versionsstand der Daten gewartet werden kann, allerdings muss dazu die aktuelle BinLog-Position erst vom Master ausgelesen werden und selbst dann können die nach dem Auslesen erfolgten Änderungen schon repliziert worden sein - oder eben nicht.

2. Ein mySQL-Slave hat niemals die gleichen Daten wie sein Master!

Moment, wir reden doch über eine Replikation, oder nicht? Alle Daten werden kopiert, woher sollen also Unterschiede kommen? Theoretisch gibt es keine, praktisch allerdings schon.

Das einfachste Beispiel ist ein UPDATE foo SET bar=2 WHERE bar=1 LIMIT 10. Wird dieses Query repliziert, dann führt jeder Slave die gleichen Änderungen aus: Er setzt bei 10 Datensätzen die Spalte "bar" auf 2, welche 10 Datensätze allerdings geändert werden, entscheidet jeder Datenbankserver selbstständig. Es können die gleichen sein, um so mehr Treffer die WHERE-Condition produziert, um so größer wird die Garantie für abweichende Daten.

Auch auto-increment-Spalten weichen gerne zwischen Master und Slave ab. Theoretisch zwar unmöglich, praktisch kommt auch das vor.

Beide Probleme treten so oft auf, dass man sie nicht einfach ignorieren kann, aber immer noch selten genug, um eine mySQL-Replikation noch nutzbar zu machen.

Die Replikation nutzen

Einfach alle SELECT's vom Master auf den Slave umstellen ist keine Lösung (siehe oben). Ein Standard-Web-Anwendungsfall verdeutlicht das Problem recht anschaulich: Eine Script zeigt eine Tabelle als Webseite an. Wird jetzt ein Datensatz hinzugefügt, dann geht das INSERT-Query zunächst an den Master und wird von diesem an alle Slaves weitergeschickt. Egal, ob als Ergebnis die Tabelle angezeigt wird oder ein "Bearbeiten"-Formular - wenn diese ohne weitere Vorkehrungen vom Slave gelesen werden, ist nahezu immer davon auszugehen, dass dieser den neuen Datensatz noch nicht erhalten hat. Das Ergebnis ist eine Tabelle ohne den gerade angelegten Datensatz oder ein leeres "Bearbeiten"-Formular.

Im besten Fall geht der Benutzer von einem Fehler aus und erstellt einen zweiten neuen Datensatz, im schlimmsten Fall bemerkt er das Problem nicht (weil die im "Bearbeiten"-Formular angezeigten Daten beispielsweise nicht der Datenbank, sondern denm vorher abgesendeten Formular entstammen) und zerstört beim Speichern eventueller Änderungen den neuen Datensatz in Teilen oder komplett (weil Felder gespeichert werden, denen die aktuellen Werte aus der Datenbank fehlen).

Jedes Query muss also einzeln überprüft werden. Die Statistiken von letzter Woche ändern sich normalerweise nicht und wird der Slave von einem Monitoringsystem überwacht, fällt ein Problem auch rechtzeitig auf. Wenn aktuellere Daten abgefragt werden sollen, muss - per SHOW SLAVE STATUS - sichergestellt werden, dass der Slave tatsächlich "frisch" genug ist - andernfalls muss das Script warten oder darf den Slave nicht benutzen.

Dabei macht es durchaus Sinn, sich Gedanken über die Aktualität zu machen. Einfache, kurze Queries die immer aktuelle Daten brauchen (z.B. SELECT email FROM USERS WHERE id=?) haben normalerweise auf dem Slave nichts zu suchen, denn um immer aktuelle Daten zu bekommen, müsste jedes Script warten bis der Slave zumindest halbwegs auf "frisch" ist. Daten, die eine Minute oder ältern sein dürfen, können in der Regel problemlos vom Slave genutzt werden.

 

 

2 Kommentare. Schreib was dazu

  1. Alexander Hartmaier (abraxxa)

    @2: das ist ja absolut lächerlich! Wenn eine Datenbankreplikation das nicht gewährleisten kann ist sie unbrauchbar und kann niemals verwendet werden!
    Das muss z.B. wie bei Oracle mit internen rowids und archivelogs gelöst werden.
    Ich frag mich wie jemand MySQL verwenden kann da sogar SQLite mehr richtig macht...

  2. Andreas Faust

    @2:
    Es gibt 2 Betriebsmodi bei MySQL. Row based Replication ( damit hat man das Problem nicht ) und statement-based-replication, bei der einfach das query kopiert wird. Das führt zu problemen, wenn die Daten zwischen master und Slave auseinanderdriften ( z.b. servercrashes, versehentliche inserts in den slave, usw. )

Schreib was dazu

Die folgenden HTML-Tags sind erlaubt:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>