Loading…

Blog

Viel Spass beim Lesen, vielleicht findest Du ja ein paar nützliche Informationen
Los geht's

Duplicate Entry in einer MySQL Replication – Wie repariere ich eine Replikation

Datenbankaufbau

Die aktuelle Situation sind 2 Datenbanken, die jeweils als Master Slave Replikation eingerichtet sind. Hierdurch haben wir eine Konfiguration in der beide Datenbanken als Master und Slave fungieren.

Wenn in dieser Verbund ein „Duplicate Key“ Fehler in der Replikation auftaucht steht man vor einen kleinen Problem.

Wie repapiere ich eine Datenbank Replikation

Der erste Schritt ist ein Weg auf die MySql Konsole.

Ein erster Überblick auf den Replikation Status

Über den Befehl SHOW SLAVE STATUS;kann man sich den aktuelle Status der Slave Replikation holen.

Der erste Blick solte auf Slave_IO_Running & Slave_SQL_Running gehen.

  • Slave_IO_Running     YES
    Slave_SQL_Running YES
    Alles OK. Server und Slave laufen einwandfrei
  • Slave_IO_Running     YES
    Slave_SQL_Running NO
    Server  läufen einwandfrei, aber die Replikation mit dem Master hängt
  • Slave_IO_Running     NO
    Slave_SQL_Running NO
    Der Server  hängt oder ist nicht gestartet.

Wir gehen davon aus, dass der Slave läuft, aber die Replikation mit dem Master hängt. Warum dies der Fall ist sagt uns das Feld Last_Error. 

In unserem Fall haben wir eine „Duplicate Key“ in Tabelle Blah in Spalte Blub auf Datenbank 1.
Warum und wie dieser Fehler entstanden ist, ist hier nicht von Belang. Das wichtige ist, dass der Fehler aufgelöst wird.

Replikationsfehler auflösen

Hier ist der erste Fallstrick. Durch die gegenseitige Replikation, können wir nicht einfach den Datensatz auf Datenbank 1 löschen, den durch die Replikation wird er später auf Datenbank 2 auch gelöscht, und das wollen wir nicht wirklich.

An dieser Stelle kommt das setzen des Slave Parameter:
mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = X;
zum Einsatz. Hiermit haben wir die Möglichkeit dem Slave mitzuteilen, dass dieser X Queries in der Replikation überspringen soll.

In unserem Fall überprüfen wir den doppelten Datensatz, und vergleichen die Einträge und notieren uns deren den Inhalt von Datenbank 1 & 2 um diese im nächsten Step wieder gerade zu rücken. Da es sich nur um einen Datensatz handelt ist das X Value des SKIP_COUNTER auf 1 zu setzen.
Nun kommt der der Ablauf auf Datenbank 1

  1. Slave stoppen
  2. SKIP_COUNTER setzen
  3. Slave starten

mysql> SLAVE STOP;
mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
mysql> SLAVE START;

Nun sollte die Replikation wieder laufen und wir müssen den Datensatz über ein Update auf beiden Datenbanken wieder auf den gleichen Stand bringen.

Achtung: Wenn mehrere Queries nötig sind um das Problem zu beheben, muss das SKIP_COUNTER Value höher gesetzt werden. Damit auch nur die Queries übersprungen werden, die für das Auflösen des Replikationsfehler benötigt werden, müssen für den Zeitpunkt die Tabellen auf der Datenbank 2 gesperrt werden, sonst könnten aus Versehen die falschen Slave Queries für die Replikation gedropt werden, und die daraus resultierende Inkonsistenz wäre größer als vorher.

TIP zur Konsolenansicht

Die Übersichtlichkeit leidet unter den vielen Tabellenspalten, so dass man nicht wirklich eine Zuordnung zwischen Spalte und Wert erkennen kann.  Hier ein Beispiel von:  SELECT * FROM user LIMIT 1;

+—————-+——————+——————————————-+————-+————-+————-+————-+————-+———–+————-+—————+————–+———–+————+—————–+————+————+————–+————+———————–+——————+————–+—————–+——————+——————+—————-+———————+——————–+——————+————+————–+———-+————+————-+————–+—————+————-+—————–+———————-+
| Host           | User             | Password                                  | Select_priv | Insert_priv | Update_priv | Delete_priv | Create_priv | Drop_priv | Reload_priv | Shutdown_priv | Process_priv | File_priv | Grant_priv | References_priv | Index_priv | Alter_priv | Show_db_priv | Super_priv | Create_tmp_table_priv | Lock_tables_priv | Execute_priv | Repl_slave_priv | Repl_client_priv | Create_view_priv | Show_view_priv | Create_routine_priv | Alter_routine_priv | Create_user_priv | Event_priv | Trigger_priv | ssl_type | ssl_cipher | x509_issuer | x509_subject | max_questions | max_updates | max_connections | max_user_connections |
+—————-+——————+——————————————-+————-+————-+————-+————-+————-+———–+————-+—————+————–+———–+————+—————–+————+————+————–+————+———————–+——————+————–+—————–+——————+——————+—————-+———————+——————–+——————+————+————–+———-+————+————-+————–+—————+————-+—————–+———————-+
| localhost      | root             | *8235EKSSE8CB399FA7573D4FEE0POJ565 | Y           | Y           | Y           | Y           | Y           | Y         | Y           | Y             | Y            | Y         | Y          | Y               | Y          | Y          | Y            | Y          | Y                     | Y                | Y            | Y               | Y                | Y                | Y              | Y                   | Y                  | Y                | Y          | Y            |          |            |             |              |             0 |           0 |               0 |                    0 |

 

Hierfür gibt es den Parameter  \G. Mit diesem Parameter wird jeder einzelne Datensatz nicht in der tabellarischen Ansicht dargestellt, sondern als Liste. Hier die Ansicht von:   SELECT * FROM user LIMIT 1 \G;

*************************** 1. row ***************************
Host: localhost
User: root
Password: *8235EKSSE8CB399FA7573D4FEE0POJ565
Select_priv: Y
Insert_priv: Y
Update_priv: Y
Delete_priv: Y
Create_priv: Y
Drop_priv: Y
Reload_priv: Y
Shutdown_priv: Y
Process_priv: Y
File_priv: Y
Grant_priv: Y
References_priv: Y
Index_priv: Y
…. und so weiter