MariaDB
 sql >> Datenbank >  >> RDS >> MariaDB

Verwendung von MariaDB Flashback auf einem MySQL-Server

MariaDB hat ein sehr cooles Feature namens Flashback eingeführt. Flashback ist eine Funktion, mit der Instanzen, Datenbanken oder Tabellen auf einen alten Snapshot zurückgesetzt werden können. Traditionell würde man zur Durchführung einer Point-in-Time-Recovery (PITR) eine Datenbank aus einer Sicherung wiederherstellen und die Binärprotokolle wiedergeben, um den Datenbankstatus zu einem bestimmten Zeitpunkt oder an einer bestimmten Position fortzuschreiben.

Mit Flashback kann die Datenbank auf einen Zeitpunkt in der Vergangenheit zurückgesetzt werden, was viel schneller geht, wenn wir nur die Vergangenheit sehen möchten, die gerade vor nicht allzu langer Zeit passiert ist. Gelegentlich kann die Verwendung von Flashback ineffizient sein, wenn Sie eine sehr alte Momentaufnahme Ihrer Daten relativ zum aktuellen Datum und zur aktuellen Uhrzeit sehen möchten. Die Wiederherstellung von einem verzögerten Slave oder von einem Backup plus das erneute Abspielen des Binärlogs sind möglicherweise die besseren Optionen.

Diese Funktion ist nur im MariaDB-Clientpaket verfügbar, aber das bedeutet nicht, dass wir sie nicht mit unseren MySQL-Servern verwenden können. Dieser Blogbeitrag zeigt, wie wir diese erstaunliche Funktion auf einem MySQL-Server verwenden können.

MariaDB-Flashback-Anforderungen

Für diejenigen, die die MariaDB-Flashback-Funktion zusätzlich zu MySQL verwenden möchten, können wir grundsätzlich Folgendes tun:

  1. Aktivieren Sie das Binärprotokoll mit der folgenden Einstellung:
    1. binlog_format =ROW (Standard seit MySQL 5.7.7).
    2. binlog_row_image =FULL (Standard seit MySQL 5.6).
  2. Verwenden Sie das Dienstprogramm msqlbinlog von einer beliebigen Installation von MariaDB 10.2.4 und höher.
  3. Flashback wird derzeit nur über DML-Anweisungen (INSERT, DELETE, UPDATE) unterstützt. Eine kommende Version von MariaDB wird Unterstützung für Flashback über DDL-Anweisungen (DROP, TRUNCATE, ALTER usw.) hinzufügen, indem die aktuelle Tabelle in eine reservierte und versteckte Datenbank kopiert oder verschoben wird und dann bei Verwendung von Flashback kopiert oder zurückversetzt wird.

Der Flashback wird erreicht, indem die vorhandene Unterstützung für Binärprotokolle im vollständigen Image-Format genutzt wird, wodurch alle Speicher-Engines unterstützt werden. Beachten Sie, dass die Flashback-Ereignisse im Speicher gespeichert werden. Stellen Sie daher sicher, dass Ihr Server über genügend Speicher für diese Funktion verfügt.

Wie funktioniert MariaDB Flashback?

Das mysqlbinlog-Dienstprogramm von MariaDB enthält zwei zusätzliche Optionen für diesen Zweck:

  • -B, --flashback – Die Flashback-Funktion kann Ihre festgeschriebenen Daten auf einen bestimmten Zeitpunkt zurücksetzen.
  • -T, --table=[name] – Listet Einträge nur für diese Tabelle auf (nur lokales Protokoll).

Indem wir die mysqlbinlog-Ausgabe mit und ohne das Flag --flashback vergleichen, können wir leicht verstehen, wie es funktioniert. Stellen Sie sich vor, die folgende Anweisung wird auf einem MariaDB-Server ausgeführt:

MariaDB> DELETE FROM sbtest.sbtest1 WHERE id = 1;

Ohne Flashback-Flag sehen wir das tatsächliche DELETE-Binlog-Ereignis:

$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 10 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000003

...
# at 453196541
#200227 12:58:18 server id 37001  end_log_pos 453196766 CRC32 0xdaa248ed Delete_rows: table id 238 flags: STMT_END_F

BINLOG '
6rxXXhOJkAAAQwAAAP06AxsAAO4AAAAAAAEABnNidGVzdAAHc2J0ZXN0MQAEAwP+/gTu4P7wAAEB
AAID/P8AFuAQfA==
6rxXXiCJkAAA4QAAAN47AxsAAO4AAAAAAAEAAgAE/wABAAAAVJ4HAHcAODM4Njg2NDE5MTItMjg3
NzM5NzI4MzctNjA3MzYxMjA0ODYtNzUxNjI2NTk5MDYtMjc1NjM1MjY0OTQtMjAzODE4ODc0MDQt
NDE1NzY0MjIyNDEtOTM0MjY3OTM5NjQtNTY0MDUwNjUxMDItMzM1MTg0MzIzMzA7Njc4NDc5Njcz
NzctNDgwMDA5NjMzMjItNjI2MDQ3ODUzMDEtOTE0MTU0OTE4OTgtOTY5MjY1MjAyOTHtSKLa
'/*!*/;

### DELETE FROM `sbtest`.`sbtest1`
### WHERE
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2=499284 /* INT meta=0 nullable=0 is_null=0 */
###   @3='83868641912-28773972837-60736120486-75162659906-27563526494-20381887404-41576422241-93426793964-56405065102-33518432330' /* STRING(480) meta=61152 nullable=0 is_null=0 */
###   @4='67847967377-48000963322-62604785301-91415491898-96926520291' /* STRING(240) meta=65264 nullable=0 is_null=0 */
...

Durch Erweitern des obigen mysqlbinlog-Befehls mit --flashback können wir sehen, dass das DELETE-Ereignis in ein INSERT-Ereignis umgewandelt wird, und ähnlich wie die entsprechenden WHERE- und SET-Klauseln:

$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 10 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000003 \
--flashback

...
BINLOG '
6rxXXhOJkAAAQwAAAP06AxsAAO4AAAAAAAEABnNidGVzdAAHc2J0ZXN0MQAEAwP+/gTu4P7wAAEB
AAID/P8AFuAQfA==
6rxXXh6JkAAA4QAAAN47AxsAAO4AAAAAAAEAAgAE/wABAAAAVJ4HAHcAODM4Njg2NDE5MTItMjg3
NzM5NzI4MzctNjA3MzYxMjA0ODYtNzUxNjI2NTk5MDYtMjc1NjM1MjY0OTQtMjAzODE4ODc0MDQt
NDE1NzY0MjIyNDEtOTM0MjY3OTM5NjQtNTY0MDUwNjUxMDItMzM1MTg0MzIzMzA7Njc4NDc5Njcz
NzctNDgwMDA5NjMzMjItNjI2MDQ3ODUzMDEtOTE0MTU0OTE4OTgtOTY5MjY1MjAyOTHtSKLa
'/*!*/;

### INSERT INTO `sbtest`.`sbtest1`
### SET
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2=499284 /* INT meta=0 nullable=0 is_null=0 */
###   @3='83868641912-28773972837-60736120486-75162659906-27563526494-20381887404-41576422241-93426793964-56405065102-33518432330' /* STRING(480) meta=61152 nullable=0 is_null=0 */
###   @4='67847967377-48000963322-62604785301-91415491898-96926520291' /* STRING(240) meta=65264 nullable=0 is_null=0 */
...

Bei der zeilenbasierten Replikation (binlog_format=ROW) enthält jedes Zeilenänderungsereignis zwei Bilder, ein „Vorher“-Bild (außer INSERT), dessen Spalten bei der Suche nach der zu aktualisierenden Zeile abgeglichen werden, und ein „Nachher“-Image (außer DELETE), das die Änderungen enthält. Mit binlog_row_image=FULL protokolliert MariaDB vollständige Zeilen (d. h. alle Spalten) sowohl für die Vorher- als auch für die Nachher-Bilder.

Das folgende Beispiel zeigt Binärprotokollereignisse für UPDATE. Stellen Sie sich vor, die folgende Anweisung wird auf einem MariaDB-Server ausgeführt:

MariaDB> UPDATE sbtest.sbtest1 SET k = 0 WHERE id = 5;

Wenn wir uns das Binlog-Ereignis für die obige Anweisung ansehen, sehen wir etwa Folgendes:

$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 5 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000001 

...
### UPDATE `sbtest`.`sbtest1`
### WHERE
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=499813 /* INT meta=0 nullable=0 is_null=0 */
###   @3='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */
###   @4='34551750492-67990399350-81179284955-79299808058-21257255869' /* STRING(240) meta=65264 nullable=0 is_null=0 */
### SET
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=0 is_null=0 */
###   @3='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */
###   @4='34551750492-67990399350-81179284955-79299808058-21257255869' /* STRING(240) meta=65264 nullable=0 is_null=0 */
# Number of rows: 1
...

Mit dem Flag --flashback wird das "Vorher"-Bild mit dem "Nachher"-Bild der bestehenden Zeile ausgetauscht:

$ mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 5 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000001 \
 --flashback

...
### UPDATE `sbtest`.`sbtest1`
### WHERE
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=0 is_null=0 */
###   @3='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */
###   @4='34551750492-67990399350-81179284955-79299808058-21257255869' /* STRING(240) meta=65264 nullable=0 is_null=0 */
### SET
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=499813 /* INT meta=0 nullable=0 is_null=0 */
###   @3='44257470806-17967007152-32809666989-26174672567-29883439075-95767161284-94957565003-35708767253-53935174705-16168070783' /* STRING(480) meta=61152 nullable=0 is_null=0 */
###   @4='34551750492-67990399350-81179284955-79299808058-21257255869' /* STRING(240) meta=65264 nullable=0 is_null=0 */
...

Wir können dann die Flashback-Ausgabe an den MySQL-Client umleiten und so die Datenbank oder Tabelle auf den gewünschten Zeitpunkt zurücksetzen. Weitere Beispiele werden in den nächsten Abschnitten gezeigt.

MariaDB hat eine spezielle Wissensdatenbankseite für diese Funktion. Sehen Sie sich die MariaDB-Flashback-Wissensdatenbankseite an.

MariaDB-Flashback mit MySQL

Um die Flashback-Fähigkeit für MySQL zu haben, muss man folgendes tun:

  • Kopieren Sie das Dienstprogramm mysqlbinlog von einem beliebigen MariaDB-Server (10.2.4 oder höher).
  • Deaktivieren Sie MySQL GTID, bevor Sie die Flashback-SQL-Datei anwenden. Seit MySQL 5.7.5 können die globalen Variablen „gtid_mode“ und „enforce_gtid_consistency“ zur Laufzeit gesetzt werden.

Angenommen, wir haben die folgende einfache MySQL 8.0-Replikationstopologie:

In diesem Beispiel haben wir das Dienstprogramm mysqlbinlog aus der neuesten MariaDB 10.4 auf einen unserer MySQL 8.0-Slaves (slave2) kopiert:

(mariadb-server)$ scp /bin/mysqlbinlog [email protected]:/root/
(slave2-mysql8)$ ls -l /root/mysqlbinlog
-rwxr-xr-x. 1 root root 4259504 Feb 27 13:44 /root/mysqlbinlog

Das mysqlbinlog-Dienstprogramm unserer MariaDB befindet sich jetzt unter /root/mysqlbinlog auf slave2. Auf dem MySQL-Master haben wir die folgende katastrophale Anweisung ausgeführt:

mysql> DELETE FROM sbtest1 WHERE id BETWEEN 5 AND 100;
Query OK, 96 rows affected (0.01 sec)

In der obigen Anweisung wurden 96 Zeilen gelöscht. Warten Sie ein paar Sekunden, damit die Ereignisse vom Master auf alle Slaves repliziert werden, bevor wir versuchen können, die Binlog-Position des katastrophalen Ereignisses auf dem Slave-Server zu finden. Der erste Schritt besteht darin, alle Binärprotokolle auf diesem Server abzurufen:

mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |       850 |        No |
| binlog.000002 |     18796 |        No |
+---------------+-----------+-----------+

Unser katastrophales Ereignis sollte in binlog.000002 existieren, dem neuesten Binärlog auf diesem Server. Wir können dann das mysqlbinlog-Dienstprogramm von MariaDB verwenden, um alle binlog-Ereignisse für die Tabelle sbtest1 seit vor 10 Minuten abzurufen:

(slave2-mysql8)$ /root/mysqlbinlog -vv \
--start-datetime="$(date '+%F %T' -d 'now - 10 minutes')" \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000002

...
# at 195
#200228 15:09:45 server id 37001  end_log_pos 281 CRC32 0x99547474 Ignorable
# Ignorable event type 33 (MySQL Gtid)
# at 281
#200228 15:09:45 server id 37001  end_log_pos 353 CRC32 0x8b12bd3c Query thread_id=19 exec_time=0 error_code=0
SET TIMESTAMP=1582902585/*!*/;
SET @@session.pseudo_thread_id=19/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1, @@session.check_constraint_checks=1/*!*/;
SET @@session.sql_mode=524288/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=255/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;

BEGIN
/*!*/;
# at 353
#200228 15:09:45 server id 37001  end_log_pos 420 CRC32 0xe0e44a1b Table_map: `sbtest`.`sbtest1` mapped to number 92

# at 420
# at 8625
# at 16830
#200228 15:09:45 server id 37001  end_log_pos 8625 CRC32 0x99b1a8fc Delete_rows: table id 92
#200228 15:09:45 server id 37001  end_log_pos 16830 CRC32 0x89496a07 Delete_rows: table id 92
#200228 15:09:45 server id 37001  end_log_pos 18765 CRC32 0x302413b2 Delete_rows: table id 92 flags: STMT_END_F

Um die Binlog-Positionsnummer einfach nachzuschlagen, achten Sie auf die Zeilen, die mit "# at " beginnen. Aus den obigen Zeilen können wir sehen, dass das DELETE-Ereignis an Position 281 in binlog.000002 stattfand (beginnt bei „# bei 281“). Wir können die Binlog-Ereignisse auch direkt in einem MySQL-Server abrufen:

mysql> SHOW BINLOG EVENTS IN 'binlog.000002';
+---------------+-------+----------------+-----------+-------------+-------------------------------------------------------------------+
| Log_name      | Pos   | Event_type     | Server_id | End_log_pos | Info                                                              |
+---------------+-------+----------------+-----------+-------------+-------------------------------------------------------------------+
| binlog.000002 |     4 | Format_desc    |     37003 | 124         | Server ver: 8.0.19, Binlog ver: 4                                 |
| binlog.000002 |   124 | Previous_gtids |     37003 | 195         | 0d98d975-59f8-11ea-bd30-525400261060:1                            |
| binlog.000002 |   195 | Gtid           |     37001 | 281         | SET @@SESSION.GTID_NEXT= '0d98d975-59f8-11ea-bd30-525400261060:2' |
| binlog.000002 |   281 | Query          |     37001 | 353         | BEGIN                                                             |
| binlog.000002 |   353 | Table_map      |     37001 | 420         | table_id: 92 (sbtest.sbtest1)                                     |
| binlog.000002 |   420 | Delete_rows    |     37001 | 8625        | table_id: 92                                                      |
| binlog.000002 |  8625 | Delete_rows    |     37001 | 16830       | table_id: 92                                                      |
| binlog.000002 | 16830 | Delete_rows    |     37001 | 18765       | table_id: 92 flags: STMT_END_F                                    |
| binlog.000002 | 18765 | Xid            |     37001 | 18796       | COMMIT /* xid=171006 */                                           |
+---------------+-------+----------------+-----------+-------------+-------------------------------------------------------------------+

9 rows in set (0.00 sec)

Wir können jetzt bestätigen, dass Position 281 die Stelle ist, an die unsere Daten zurückkehren sollen. Wir können dann das Flag --start-position verwenden, um genaue Flashback-Ereignisse zu generieren. Beachten Sie, dass wir das Flag „-vv“ weglassen und fügen Sie das Flag --flashback hinzu:

(slave2-mysql8)$ /root/mysqlbinlog \
--start-position=281 \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000002 \
--flashback > /root/flashback.binlog

Das flashback.binlog enthält alle erforderlichen Ereignisse, um alle Änderungen rückgängig zu machen, die an Tabelle sbtest1 auf diesem MySQL-Server vorgenommen wurden. Da dies ein Slave-Knoten eines Replikationsclusters ist, müssen wir die Replikation auf dem ausgewählten Slave (Slave2) unterbrechen, um sie für Flashback-Zwecke zu verwenden. Dazu müssen wir die Replikation auf dem ausgewählten Slave stoppen, MySQL GTID auf ON_PERMISSIVE setzen und den Slave beschreibbar machen:

mysql> STOP SLAVE; 
SET GLOBAL gtid_mode = ON_PERMISSIVE; 
SET GLOBAL enforce_gtid_consistency = OFF; 
SET GLOBAL read_only = OFF;

Zu diesem Zeitpunkt ist slave2 nicht Teil der Replikation und unsere Topologie sieht so aus:

Importieren Sie das Flashback über den MySQL-Client und wir möchten nicht, dass diese Änderung vorgenommen wird aufgezeichnet im MySQL-Binärlog:

(slave2-mysql8)$ mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest < /root/flashback.binlog

Wir können dann alle gelöschten Zeilen sehen, wie die folgende Anweisung beweist:

mysql> SELECT COUNT(id) FROM sbtest1 WHERE id BETWEEN 5 and 100;
+-----------+
| COUNT(id) |
+-----------+
|        96 |
+-----------+
1 row in set (0.00 sec)

Als Referenz können wir dann eine SQL-Dump-Datei für die Tabelle sbtest1 erstellen:

(slave2-mysql8)$ mysqldump -uroot -p --single-transaction sbtest sbtest1 > sbtest1_flashbacked.sql

Sobald der Flashback-Vorgang abgeschlossen ist, können wir den Slave-Knoten wieder in die Replikationskette einfügen. Aber zuerst müssen wir die Datenbank wieder in einen konsistenten Zustand bringen, indem wir alle Ereignisse ab der Position wiederholen, die wir im Flashback hatten. Vergessen Sie nicht, die binäre Protokollierung zu überspringen, da wir nicht auf den Slave "schreiben" und uns mit fehlerhaften Transaktionen riskieren wollen:

(slave2-mysql8)$ /root/mysqlbinlog \
--start-position=281 \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000002 | mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest

Zu guter Letzt bereiten Sie den Knoten wieder auf seine Rolle als MySQL-Slave vor und starten die Replikation:

mysql> SET GLOBAL read_only = ON;
SET GLOBAL enforce_gtid_consistency = ON; 
SET GLOBAL gtid_mode = ON; 
START SLAVE; 

Überprüfen Sie, ob der Slave-Knoten korrekt repliziert:

mysql> SHOW SLAVE STATUS\G
...
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
...

An diesem Punkt haben wir den Slave wieder in die Replikationskette aufgenommen und unsere Topologie befindet sich nun wieder in ihrem ursprünglichen Zustand:

Danke an das MariaDB-Team für die Einführung dieser erstaunlichen Funktion!