MariaDB
 sql >> Database >  >> RDS >> MariaDB

Utilizzo di MariaDB Flashback su un server MySQL

MariaDB ha introdotto una funzione molto interessante chiamata Flashback. Flashback è una funzionalità che consentirà il rollback di istanze, database o tabelle su un vecchio snapshot. Tradizionalmente, per eseguire un ripristino point-in-time (PITR), è necessario ripristinare un database da un backup e riprodurre i registri binari per eseguire il rollforward dello stato del database in un determinato momento o posizione.

Con Flashback, il database può essere ripristinato a un punto del tempo nel passato, il che è molto più veloce se vogliamo solo vedere il passato che è appena accaduto non molto tempo fa. Occasionalmente, l'uso del flashback potrebbe essere inefficiente se si desidera visualizzare uno snapshot molto vecchio dei dati rispetto alla data e all'ora correnti. Il ripristino da uno slave ritardato o da un backup più la riproduzione del registro binario potrebbero essere le opzioni migliori.

Questa funzione è disponibile solo nel pacchetto client MariaDB, ma ciò non significa che non possiamo utilizzarla con i nostri server MySQL. Questo post sul blog mostra come possiamo utilizzare questa straordinaria funzionalità su un server MySQL.

Requisiti per il flashback di MariaDB

Per coloro che desiderano utilizzare la funzione di flashback di MariaDB su MySQL, possiamo sostanzialmente fare quanto segue:

  1. Abilita registro binario con la seguente impostazione:
    1. binlog_format =RIGA (impostazione predefinita da MySQL 5.7.7).
    2. binlog_row_image =FULL (impostazione predefinita da MySQL 5.6).
  2. Utilizzare l'utilità msqlbinlog da qualsiasi installazione di MariaDB 10.2.4 e successive.
  3. Il flashback è attualmente supportato solo su istruzioni DML (INSERT, DELETE, UPDATE). Una prossima versione di MariaDB aggiungerà il supporto per il flashback su istruzioni DDL (DROP, TRUNCATE, ALTER, ecc.) copiando o spostando la tabella corrente in un database riservato e nascosto, quindi copiando o tornando indietro quando si utilizza il flashback.

Il flashback si ottiene sfruttando il supporto esistente per i registri binari in formato immagine completo, quindi supporta tutti i motori di archiviazione. Si noti che gli eventi di flashback verranno archiviati in memoria. Pertanto, dovresti assicurarti che il tuo server abbia memoria sufficiente per questa funzione.

Come funziona MariaDB Flashback?

L'utilità mysqlbinlog di MariaDB include due opzioni extra per questo scopo:

  • -B, --flashback - La funzione Flashback può eseguire il rollback dei dati impegnati in un momento speciale.
  • -T, --table=[name] - Elenca le voci solo per questa tabella (solo registro locale).

Confrontando l'output di mysqlbinlog con e senza il flag --flashback, possiamo facilmente capire come funziona. Considera che la seguente istruzione viene eseguita su un server MariaDB:

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

Senza flag di flashback, vedremo l'evento binlog DELETE effettivo:

$ 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 */
...

Estendendo il comando mysqlbinlog sopra con --flashback, possiamo vedere che l'evento DELETE viene convertito in un evento INSERT e in modo simile alle rispettive clausole WHERE e 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 */
...

Nella replica basata su riga (binlog_format=ROW), ogni evento di modifica riga contiene due immagini, un'immagine "prima" (tranne INSERT) le cui colonne vengono confrontate durante la ricerca della riga da aggiornare, e un'immagine "dopo" (tranne DELETE) contenente le modifiche. Con binlog_row_image=FULL, MariaDB registra le righe complete (ovvero tutte le colonne) sia per le immagini prima che dopo.

L'esempio seguente mostra gli eventi di log binari per UPDATE. Considera che la seguente istruzione viene eseguita su un server MariaDB:

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

Quando esaminiamo l'evento binlog per l'istruzione sopra, vedremo qualcosa del genere:

$ 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
...

Con il flag --flashback, l'immagine "prima" viene scambiata con l'immagine "dopo" della riga esistente:

$ 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 */
...

Possiamo quindi reindirizzare l'output del flashback al client MySQL, ripristinando così il database o la tabella al momento desiderato. Altri esempi sono mostrati nelle sezioni successive.

MariaDB ha una pagina di knowledge base dedicata per questa funzione. Dai un'occhiata alla pagina della knowledge base di MariaDB Flashback.

Flashback di MariaDB con MySQL

Per avere la capacità di flashback per MySQL, devi fare quanto segue:

  • Copia l'utilità mysqlbinlog da qualsiasi server MariaDB (10.2.4 o successivo).
  • Disabilita MySQL GTID prima di applicare il file SQL di flashback. Le variabili globali gtid_mode e force_gtid_consistency possono essere impostate in runtime a partire da MySQL 5.7.5.

Supponiamo di avere la seguente semplice topologia di replica MySQL 8.0:

In questo esempio, abbiamo copiato l'utilità mysqlbinlog dall'ultima MariaDB 10.4 su uno dei nostri 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

L'utilità mysqlbinlog di MariaDB ora si trova in /root/mysqlbinlog su slave2. Sul master MySQL, abbiamo eseguito la seguente disastrosa istruzione:

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

96 righe sono state cancellate nella dichiarazione di cui sopra. Attendi un paio di secondi per consentire agli eventi di replicarsi dal master a tutti gli slave prima di poter provare a trovare la posizione binlog dell'evento disastroso sul server slave. Il primo passo è recuperare tutti i log binari su quel server:

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

Il nostro disastroso evento dovrebbe esistere all'interno di binlog.000002, l'ultimo registro binario in questo server. Possiamo quindi utilizzare l'utilità mysqlbinlog di MariaDB per recuperare tutti gli eventi binlog per la tabella sbtest1 da 10 minuti fa:

(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

Per cercare facilmente il numero della posizione del binlog, fai attenzione alle righe che iniziano con "# at ". Dalle righe precedenti, possiamo vedere che l'evento DELETE si stava verificando nella posizione 281 all'interno di binlog.000002 (inizia da "# a 281"). Possiamo anche recuperare gli eventi binlog direttamente all'interno di un server 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)

Ora possiamo confermare che la posizione 281 è dove vogliamo che i nostri dati tornino. Possiamo quindi utilizzare il flag --start-position per generare eventi di flashback accurati. Nota che omettiamo il flag "-vv" e aggiungi il flag --flashback:

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

Il flashback.binlog contiene tutti gli eventi richiesti per annullare tutte le modifiche avvenute sulla tabella sbtest1 su questo server MySQL. Poiché si tratta di un nodo slave di un cluster di replica, dobbiamo interrompere la replica sullo slave scelto (slave2) per utilizzarlo per scopi di flashback. Per fare ciò, dobbiamo fermare la replica sullo slave scelto, impostare MySQL GTID su ON_PERMISSIVE e rendere lo slave scrivibile:

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

A questo punto, slave2 non fa parte della replica e la nostra topologia è simile a questa:

Importa il flashback tramite il client mysql e non vogliamo che questa modifica sia registrato nel log binario di MySQL:

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

Possiamo quindi vedere tutte le righe eliminate, come dimostrato dalla seguente affermazione:

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

Possiamo quindi creare un file di dump SQL per la tabella sbtest1 come riferimento:

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

Una volta completata l'operazione di flashback, possiamo ricongiungere il nodo slave alla catena di replica. Ma in primo luogo, dobbiamo riportare il database in uno stato coerente, riproducendo tutti gli eventi a partire dalla posizione in cui avevamo eseguito il flashback. Non dimenticare di saltare la registrazione binaria perché non vogliamo "scrivere" sullo slave e rischiare noi stessi con transazioni errate:

(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

Infine, prepara il nodo al suo ruolo di slave MySQL e avvia la replica:

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

Verifica che il nodo slave si stia replicando correttamente:

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

A questo punto, abbiamo ricollegato lo slave alla catena di replica e la nostra topologia è ora tornata al suo stato originale:

Grida al team di MariaDB per aver introdotto questa straordinaria funzionalità!