MariaDB představila velmi skvělou funkci nazvanou Flashback. Flashback je funkce, která umožní instance, databáze nebo tabulky vrátit zpět na starý snímek. Tradičně se k provedení obnovy v určitém okamžiku (PITR) obnoví databáze ze zálohy a znovu se přehrají binární protokoly, aby se posunul stav databáze v určitém čase nebo pozici.
Pomocí Flashback lze databázi vrátit zpět do určitého časového bodu v minulosti, což je mnohem rychlejší, pokud chceme vidět minulost, která se právě nedávno stala. Občas může být použití flashbacku neefektivní, pokud chcete vidět velmi starý snímek vašich dat vzhledem k aktuálnímu datu a času. Obnova ze zpožděného slave zařízení nebo ze zálohy plus přehrání binárního protokolu může být lepší možností.
Tato funkce je dostupná pouze v klientském balíčku MariaDB, ale to neznamená, že ji nemůžeme používat s našimi servery MySQL. Tento blogový příspěvek ukazuje, jak můžeme tuto úžasnou funkci použít na serveru MySQL.
Požadavky MariaDB Flashback
Pro ty, kteří chtějí používat funkci zpětného zobrazení MariaDB nad MySQL, můžeme v zásadě udělat následující:
- Povolte binární protokol s následujícím nastavením:
- binlog_format =ROW (výchozí od MySQL 5.7.7).
- binlog_row_image =FULL (výchozí od MySQL 5.6).
- Použijte nástroj msqlbinlog z libovolné instalace MariaDB 10.2.4 a novější.
- Flashback je aktuálně podporován pouze u příkazů DML (INSERT, DELETE, UPDATE). Nadcházející verze MariaDB přidá podporu pro flashback přes příkazy DDL (DROP, TRUNCATE, ALTER atd.) zkopírováním nebo přesunem aktuální tabulky do vyhrazené a skryté databáze a následným zkopírováním nebo přesunem zpět při použití flashbacku.
Flashbacku je dosaženo využitím stávající podpory pro binární protokoly plného formátu obrazu, takže podporuje všechny moduly úložiště. Všimněte si, že flashback události budou uloženy v paměti. Proto byste se měli ujistit, že váš server má pro tuto funkci dostatek paměti.
Jak funguje MariaDB Flashback?
Obslužný program mysqlbinlog společnosti MariaDB má pro tento účel dvě další možnosti:
- -B, --flashback – Funkce Flashback může vrátit vaše odevzdaná data do zvláštního časového bodu.
- -T, --table=[name] – Zobrazí položky pouze pro tuto tabulku (pouze místní protokol).
Porovnáním výstupu mysqlbinlog s příznakem --flashback a bez něj snadno pochopíme, jak to funguje. Zvažte, že následující příkaz se provádí na serveru MariaDB:
MariaDB> DELETE FROM sbtest.sbtest1 WHERE id = 1;
Bez příznaku flashbacku uvidíme skutečnou událost DELETE binlog:
$ 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 */
...
Rozšířením výše uvedeného příkazu mysqlbinlog o --flashback vidíme, že událost DELETE je převedena na událost INSERT a podobně jako příslušné klauzule WHERE a SET:
$ 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 */
...
Při replikaci založené na řádcích (binlog_format=ROW) obsahuje každá událost změny řádku dva obrázky, obrázek „před“ (kromě INSERT), jehož sloupce se porovnávají při hledání řádku, který má být aktualizován, a obrázek „po“ (kromě DELETE) obsahující změny. S binlog_row_image=FULL MariaDB zaznamená celé řádky (tj. všechny sloupce) pro obrázky před i po.
Následující příklad ukazuje události binárního protokolu pro UPDATE. Zvažte, že následující příkaz se provádí na serveru MariaDB:
MariaDB> UPDATE sbtest.sbtest1 SET k = 0 WHERE id = 5;
Když se podíváme na událost binlog pro výše uvedený příkaz, uvidíme něco takového:
$ 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
...
S příznakem --flashback se obrázek „před“ zamění za obrázek „po“ existujícího řádku:
$ 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 */
...
Potom můžeme přesměrovat výstup flashbacku na klienta MySQL, čímž vrátíme databázi nebo tabulku do požadovaného časového bodu. Další příklady jsou uvedeny v dalších částech.
MariaDB má pro tuto funkci vyhrazenou stránku znalostní báze. Podívejte se na stránku znalostní báze MariaDB Flashback.
MariaDB Flashback s MySQL
Chcete-li mít možnost flashback pro MySQL, musíte provést následující:
- Zkopírujte nástroj mysqlbinlog z libovolného serveru MariaDB (10.2.4 nebo novější).
- Před použitím flashback souboru SQL deaktivujte MySQL GTID. Globální proměnné gtid_mode a force_gtid_consistency lze nastavit za běhu od MySQL 5.7.5.
Předpokládejme, že máme následující jednoduchou topologii replikace MySQL 8.0:
V tomto příkladu jsme zkopírovali nástroj mysqlbinlog z nejnovější MariaDB 10.4 na jednom z našich slave MySQL 8.0 (slave2):
(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
Nástroj mysqlbinlog naší MariaDB je nyní umístěn na /root/mysqlbinlog na slave2. Na hlavním serveru MySQL jsme provedli následující katastrofální příkaz:
mysql> DELETE FROM sbtest1 WHERE id BETWEEN 5 AND 100;
Query OK, 96 rows affected (0.01 sec)
Ve výše uvedeném prohlášení bylo vymazáno 96 řádků. Počkejte několik sekund, aby se události replikovaly z hlavního na všechny podřízené, než se budeme moci pokusit najít pozici binlogu katastrofální události na podřízeném serveru. Prvním krokem je načtení všech binárních protokolů na tomto serveru:
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 | 850 | No |
| binlog.000002 | 18796 | No |
+---------------+-----------+-----------+
Naše katastrofální událost by měla existovat uvnitř binlog.000002, nejnovějším binárním protokolu na tomto serveru. Poté můžeme použít obslužný program mysqlbinlog MariaDB k načtení všech událostí binlog pro tabulku sbtest1 od doby před 10 minutami:
(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
Chcete-li snadno vyhledat číslo pozice binlogu, věnujte pozornost řádkům, které začínají "# na ". Z výše uvedených řádků vidíme, že událost DELETE probíhala na pozici 281 uvnitř binlog.000002 (začíná na "# na 281"). Můžeme také načíst události binlog přímo na serveru MySQL:
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)
Nyní můžeme potvrdit, že pozice 281 je místo, kam chceme, aby se naše data vrátila. Potom můžeme použít příznak --start-position ke generování přesných flashbacků. Všimněte si, že vynecháváme příznak "-vv" a přidejte příznak --flashback:
(slave2-mysql8)$ /root/mysqlbinlog \
--start-position=281 \
--database=sbtest \
--table=sbtest1 \
/var/lib/mysql/binlog.000002 \
--flashback > /root/flashback.binlog
Flashback.binlog obsahuje všechny požadované události pro vrácení všech změn, ke kterým došlo v tabulce sbtest1 na tomto serveru MySQL. Protože se jedná o podřízený uzel replikačního clusteru, musíme replikaci na vybraném podřízeném (slave2) přerušit, abychom jej mohli použít pro účely flashbacku. Abychom to mohli udělat, musíme zastavit replikaci na zvoleném slave zařízení, nastavit MySQL GTID na ON_PERMISSIVE a nastavit slave tak, aby bylo možné zapisovat:
mysql> STOP SLAVE;
SET GLOBAL gtid_mode = ON_PERMISSIVE;
SET GLOBAL enforce_gtid_consistency = OFF;
SET GLOBAL read_only = OFF;
V tuto chvíli není slave2 součástí replikace a naše topologie vypadá takto:
Importujte flashback přes klienta mysql a nechceme, aby tato změna byla zaznamenané v binárním protokolu MySQL:
(slave2-mysql8)$ mysql -uroot -p --init-command='SET sql_log_bin=0' sbtest < /root/flashback.binlog
Potom můžeme vidět všechny smazané řádky, jak dokazuje následující tvrzení:
mysql> SELECT COUNT(id) FROM sbtest1 WHERE id BETWEEN 5 and 100;
+-----------+
| COUNT(id) |
+-----------+
| 96 |
+-----------+
1 row in set (0.00 sec)
Poté můžeme vytvořit soubor výpisu SQL pro tabulku sbtest1 pro naši referenci:
(slave2-mysql8)$ mysqldump -uroot -p --single-transaction sbtest sbtest1 > sbtest1_flashbacked.sql
Jakmile bude operace flashback dokončena, můžeme se znovu připojit podřízený uzel zpět do replikačního řetězce. Nejprve však musíme uvést databázi zpět do konzistentního stavu přehráním všech událostí počínaje pozicí, kterou jsme si flashbackovali. Nezapomeňte přeskočit binární protokolování, protože nechceme "zapisovat" na slave a riskovat sami sebe chybnými transakcemi:
(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
Nakonec připravte uzel zpět na jeho roli jako MySQL slave a spusťte replikaci:
mysql> SET GLOBAL read_only = ON;
SET GLOBAL enforce_gtid_consistency = ON;
SET GLOBAL gtid_mode = ON;
START SLAVE;
Ověřte, že se podřízený uzel správně replikuje:
mysql> SHOW SLAVE STATUS\G
...
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
...
V tuto chvíli jsme znovu připojili slave zpět do replikačního řetězce a naše topologie je nyní zpět do původního stavu:
Říkejte týmu MariaDB za představení této úžasné funkce!