Erstellen Sie einen Trigger, um Tabellendaten in der Datenbank eines anderen Servers zu aktualisieren

8

Ich erstelle einen Trigger in MySQL und brauche ein wenig Hilfe.

Ich habe 2 Websites, 2 Datenbanken (gleicher Name) auf 2 verschiedenen Webservern, S1 & S2.

Diese Datenbanken haben dieselben Tabellennamen.

Ich möchte, dass beide Benutzerdaten auf beiden Websites gleich sind.

Wenn sich also ein Benutzer bei S1 registriert, sollten diese Benutzerregistrierungsinformationen an S2 übergeben werden.

Wenn eine Benutzerregistrierungsinformation auf S1 aktualisiert wird, sollten dieselben Informationen auf S2 aktualisiert werden.

Gleiches gilt für S2.

Wie kann ich einen Trigger erstellen, damit bei jedem Einfügen / Aktualisieren / Löschen in der Datenbank auf S1 auch die Benutzertabelle auf S2 automatisch aktualisiert wird?

Und jedes Mal, wenn in S2 ein Einfügen / Aktualisieren / Löschen in der Datenbank erfolgt, wird auch die Benutzertabelle in S1 automatisch aktualisiert.

Ist das möglich? Können Sie einige Beispiele nennen?

mkb
quelle
Ich denke, Sie suchen nach einer Art Replikation, nicht nach einem Auslöser. Meines Wissens funktioniert letzterer nur innerhalb derselben MySQL-Instanz. Das heißt, es ist möglich, Auslöser zu definieren, die mehr als eine Datenbank betreffen, die sich jedoch auf derselben befinden müssen Server.
Dekso
Ok Danke dezso. Gibt es eine andere Möglichkeit, wie ich dies in MySQL tun kann?
mkb
1
Außerdem - seien Sie vorsichtig. Diese Art von Updates wird am besten nur mit etwas Entkoppeltem durchgeführt, andernfalls werden die Updates in der ersten Datenbank vom zweiten Server heruntergefahren.
TomTom

Antworten:

11

Um das zu erreichen, was Sie tun möchten, können Sie die FEDERATEDSpeicher-Engine auf beiden Servern in Verbindung mit Triggern verwenden, damit jeder Server die Datenbank des anderen Servers aktualisieren kann.

Dies ist nicht gerade eine einfache Standardlösung, da hierfür zusätzliche Vorsichtsmaßnahmen erforderlich sind und Sie entscheiden müssen, ob Konsistenz oder Isolationstoleranz wichtiger sind, und die Abfragen fehlschlagen können, wenn der andere Server nicht verfügbar ist (mehr Konsistenz) ) oder verwenden Sie a CONTINUE HANDLER, um Fehler zu unterdrücken (Isolationstoleranz).

Aber hier ist ein extrem vereinfachtes Beispiel.

Jeder Server hätte die gleiche Konfiguration.

Die lokale Benutzertabelle:

CREATE TABLE user (
  username varchar(64) NOT NULL,
  password varbinary(48) NOT NULL, /* encrypted of course */
  PRIMARY KEY(username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Eine lokale Tabelle, die mit der Benutzertabelle auf dem anderen Server verbunden ist.

CREATE TABLE remote_user (
  username varchar(64) NOT NULL,
  password varbinary(48) NOT NULL, /* encrypted of course */
  PRIMARY KEY(username)
) ENGINE=FEDERATED DEFAULT CHARSET=utf8 CONNECTION='mysql://username:pass@the_other_host:port/schema/user';

Durch Auswahl von remote_user auf einem Server werden die Datensätze vom anderen Server abgerufen, und durch Einfügen / Aktualisieren / Löschen in dieser Tabelle werden die Daten auf dem anderen Server geändert.

Wir erstellen also Trigger, die den Zweck der Aktualisierung des Entfernungsservers erfüllen. Sie werden als BEFOREAuslöser geschrieben, mit der Idee, dass wir uns selbst nichts antun wollen, was wir dem anderen Server nicht antun können - zum Beispiel, wenn auf dem anderen Server bereits ein Benutzername vorhanden ist, aber nicht hier. Wir möchten, dass die Einfügung auf dem anderen Server einen Fehler auslöst, der uns daran hindert, den Benutzer hier zu erstellen ... im Gegensatz zum Erstellen eines Benutzers hier mit einem widersprüchlichen Benutzernamen. Dies ist natürlich eine der Kompromissentscheidungen, die Sie treffen müssen.

DELIMITER $$

CREATE TRIGGER user_bi BEFORE INSERT ON user FOR EACH ROW
BEGIN
  INSERT INTO remote_user (username,password) VALUES (NEW.username,NEW.password);
END $$

CREATE TRIGGER user_bu BEFORE UPDATE ON user FOR EACH ROW
BEGIN
  UPDATE remote_user 
     SET username = NEW.username,
         password = NEW.password
   WHERE username = OLD.username;
END $$

CREATE TRIGGER user_bd BEFORE DELETE ON user FOR EACH ROW
BEGIN
  DELETE FROM remote_user
   WHERE username = OLD.username;
END $$

DELIMITER ;

Dies ist keine perfekte Lösung und keine Hochverfügbarkeitslösung, da sie auf einer soliden Konnektivität zwischen den beiden Systemen beruht. Selbst wenn Sie InnoDB und Transaktionen verwenden, sind die Aktionen, die Sie für die Zieltabelle ausführen, nicht Teil Ihrer lokalen Transaktion und kann nicht zurückgesetzt werden.

Ich benutze den FEDERATEDMotor ziemlich oft; Es ist praktisch für eine Reihe kreativer Zwecke in meiner Umgebung, einschließlich einer Situation, in der ich eine von einem Auslöser gestartete Verbundabfrage verwendet habe, um einer fremden Datenquelle Fremdschlüsseleinschränkungen aufzuerlegen. Ich beschränke die Verwendung jedoch auf Back-End-Prozesse, bei denen unerwartete Probleme wie Zeitüberschreitungen, Codierungsfehler oder Server-zu-Server-Netzwerk- / Ausfall- / Isolationsereignisse nicht dazu führen können, dass der Endbenutzer auf einer unserer Websites Probleme hat . Ihre Fähigkeit, eine solche Situation zu tolerieren, wäre ein entscheidender Faktor dafür, ob dies eine geeignete Lösung ist.

Eine Alternative wäre, Ihre beiden Server in der Master / Master-Replikation zu konfigurieren. Dazu müssten Sie auf jedem Server unterschiedliche Datenbanknamen verwenden, damit bei den meisten replizierenden Ereignissen die beiden Server möglicherweise nicht miteinander in Konflikt geraten können. Im schlimmsten Fall, wenn Sie die Konnektivität verlieren oder auf einen Replikationsfehler stoßen, werden die beiden Standorte weiterhin unabhängig voneinander ausgeführt und Sie können erneut synchronisieren und wiederherstellen. Die Konfiguration würde ungefähr so ​​aussehen:

database_a database for site A
database_b database for site B
database_c database for only the shared table(s)

Dann in database_a und database_b:

CREATE ALGORITHM=MERGE SQL SECURITY INVOKER VIEW user AS SELECT * FROM c.user;

MySQL behandelt database_a.user und database_b.user als Aliase für die "echte" Benutzertabelle database_c.user, sodass Sie Ihre Anwendung nur ändern müssen, wenn Sie die angegebene Datenbank verwenden (dh nicht konfigurieren müssen) um zu verstehen, dass sich die Benutzertabelle tatsächlich in einem anderen Schema befand, da die Ansicht mit dieser Konfiguration ziemlich transparent funktioniert). Wenn die Schemas Fremdschlüssel für die Benutzertabelle haben, würden Sie diese für die wahre Basistabelle database_c.user deklarieren.

Konfigurieren Sie die beiden Server zu replizieren alles, aber Satz auto_increment_incrementund in auto_increment_offsetgeeigneter Weise, so dass Sie nicht haben widerstreit Autoinkrement-Werte auf der gemeinsamen Tabelle (n), wenn Ihre Tabellen Autoinkrement verwenden. (Beachten Sie, dass in der Dokumentation angegeben ist, dass diese Variablen nur für NDBTabellen gelten, dies jedoch nicht korrekt ist.)

Ein zusätzlicher Vorteil dieses Setups besteht darin, dass Ihre beiden Server dann über ein vollständiges Duplikat der Daten der anderen Site verfügen, die Sie möglicherweise zu Ihrem Vorteil für die Wiederherstellung nach einem Hardwarefehler auf einem der Server verwenden könnten.

Michael - sqlbot
quelle
1

Es ist nur ein Tisch von vielen, der gleich sein sollte, nicht wahr?

Trigger funktionieren also nicht, da sie nur im selben Schema auf demselben Server ausgeführt werden können.

Vermutlich haben Sie 3 Möglichkeiten:

  1. Manuelle Synchronisierung, z. B. durch Aufrufen eines Skripts, das alle Abfragen von Benutzerdaten auf S1 auf dem anderen Server ausführt. Ein bisschen hässlich.

  2. Leiten Sie alle Abfragen in dieser Tabelle an einen Server weiter, z. B. S1 enthält die Benutzertabelle, und Ihre Anwendung fragt auf Server 2 nicht die lokale Benutzertabelle auf S2 ab, sondern remote auf S1. Nicht so besser.

  3. Verwenden Sie den FEDERATED-Motor. Speichern Sie die Benutzerdaten auf S1 und erstellen Sie dieselbe Tabellenstruktur mit FEDERATED auf S2. Dies bedeutet, dass S2 nicht die physischen Benutzerdaten enthält, sondern eine Verbindung zur Tabelle auf S1. Alle an S2 durchgeführten Aktionen werden an S1 ausgeführt. Unter http://en.wikipedia.org/wiki/MySQL_Federated und http://dev.mysql.com/doc/refman/5.0/en/federated-storage-engine.html finden Sie heraus, ob dies Ihren Anforderungen entspricht.

In allen drei Szenarien ist es schwierig, eine Aussage über die Verzögerungen zwischen den Synchronisierungen zu machen. Option 3 ist die einzige, bei der Sie nur die Datenbank und nicht die Anwendung anpassen müssen, aber ich weiß wirklich nicht, ob das die große Sache ist.

32bitfloat
quelle
0

Erstens eine kurze Einschränkung: Gehen Sie wahrscheinlich den Weg der anderen Antworten und verwenden Sie den Verbund für Ihr spezifisches Setup. Das heißt, es lohnt sich in Ihrem Namen darüber nachzudenken, ob die Aufteilung der Benutzerdaten in eine dritte Datenbank eine bessere Lösung wäre.

Es werden Duplikate aus den 2 Datenbanken entfernt, aber ich würde davon ausgehen, dass es auch einiges an Nacharbeit erfordern würde, damit die beiden Sites von einer zentralen Authentifizierungsdatenbank aus funktionieren

James Sinclair
quelle
-1

Erstellen Sie einen Verbindungsserver zwischen Ihren Servern und erstellen Sie eine gespeicherte Prozedur auf beiden Servern.

Beispiel:

CREATE proc p_insert(your parameters)

...your logic

INSERT INTO table values ()
INSERT INTO [AnotherSERVERNAME].[DATABASE NAME].dbo.[Table] values()

Es kann Ihr Problem lösen.

Ashish Gupta
quelle