sql >> Base de Datos >  >> RDS >> MariaDB

Usando MariaDB Flashback en un servidor MySQL

MariaDB ha introducido una función muy interesante llamada Flashback. Flashback es una función que permitirá revertir instancias, bases de datos o tablas a una instantánea anterior. Tradicionalmente, para realizar una recuperación de un punto en el tiempo (PITR), uno restauraría una base de datos desde una copia de seguridad y reproduciría los registros binarios para avanzar el estado de la base de datos en un momento o posición determinados.

Con Flashback, la base de datos se puede revertir a un punto en el pasado, lo cual es mucho más rápido si solo queremos ver el pasado que sucedió hace poco tiempo. Ocasionalmente, el uso de flashback puede resultar ineficaz si desea ver una instantánea muy antigua de sus datos en relación con la fecha y la hora actuales. Restaurar desde un esclavo retrasado o desde una copia de seguridad más reproducir el registro binario pueden ser las mejores opciones.

Esta función solo está disponible en el paquete del cliente MariaDB, pero eso no significa que no podamos usarla con nuestros servidores MySQL. Esta publicación de blog muestra cómo podemos usar esta increíble función en un servidor MySQL.

Requisitos de retrospectiva de MariaDB

Para aquellos que quieran usar la función flashback de MariaDB además de MySQL, básicamente podemos hacer lo siguiente:

  1. Habilite el registro binario con la siguiente configuración:
    1. binlog_format =ROW (predeterminado desde MySQL 5.7.7).
    2. binlog_row_image =FULL (predeterminado desde MySQL 5.6).
  2. Utilice la utilidad msqlbinlog desde cualquier instalación de MariaDB 10.2.4 y posterior.
  3. Actualmente, Flashback solo es compatible con sentencias DML (INSERTAR, ELIMINAR, ACTUALIZAR). Una próxima versión de MariaDB agregará soporte para flashback sobre sentencias DDL (DROP, TRUNCATE, ALTER, etc.) copiando o moviendo la tabla actual a una base de datos reservada y oculta, y luego copiando o retrocediendo cuando se usa flashback.

El flashback se logra aprovechando el soporte existente para registros binarios de formato de imagen completo, por lo que es compatible con todos los motores de almacenamiento. Tenga en cuenta que los eventos de flashback se almacenarán en la memoria. Por lo tanto, debe asegurarse de que su servidor tenga suficiente memoria para esta función.

¿Cómo funciona MariaDB Flashback?

La utilidad mysqlbinlog de MariaDB viene con dos opciones adicionales para este propósito:

  • -B, --flashback:la función Flashback puede revertir los datos comprometidos a un punto de tiempo especial.
  • -T, --table=[name] - Muestra las entradas solo para esta tabla (solo registro local).

Al comparar la salida de mysqlbinlog con y sin el indicador --flashback, podemos entender fácilmente cómo funciona. Considere que la siguiente declaración se ejecuta en un servidor MariaDB:

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

Sin el indicador de flashback, veremos el evento de registro de ELIMINACIÓN real:

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

Al extender el comando mysqlbinlog anterior con --flashback, podemos ver que el evento DELETE se convierte en un evento INSERT y de manera similar a las cláusulas WHERE y SET respectivas:

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

En la replicación basada en filas (binlog_format=ROW), cada evento de cambio de fila contiene dos imágenes, una imagen "antes" (excepto INSERT) cuyas columnas se comparan cuando se busca la fila que se actualizará, y una imagen "después" (excepto ELIMINAR) que contiene los cambios. Con binlog_row_image=FULL, MariaDB registra filas completas (es decir, todas las columnas) para las imágenes anteriores y posteriores.

El siguiente ejemplo muestra eventos de registro binario para UPDATE. Considere que la siguiente declaración se ejecuta en un servidor MariaDB:

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

Al mirar el evento binlog para la declaración anterior, veremos algo como esto:

$ 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 el indicador --flashback, la imagen "antes" se intercambia con la imagen "después" de la fila existente:

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

Podemos entonces redirigir la salida del flashback al cliente MySQL, revirtiendo así la base de datos o la tabla al punto de tiempo que queramos. Se muestran más ejemplos en las siguientes secciones.

MariaDB tiene una página de base de conocimientos dedicada a esta función. Consulte la página de la base de conocimientos de MariaDB Flashback.

Flashback de MariaDB con MySQL

Para tener la capacidad de flashback para MySQL, uno tiene que hacer lo siguiente:

  • Copie la utilidad mysqlbinlog desde cualquier servidor MariaDB (10.2.4 o posterior).
  • Desactive MySQL GTID antes de aplicar el archivo flashback SQL. Las variables globales gtid_mode y enforce_gtid_consistency se pueden configurar en tiempo de ejecución desde MySQL 5.7.5.

Supongamos que tenemos la siguiente topología de replicación simple de MySQL 8.0:

En este ejemplo, copiamos la utilidad mysqlbinlog de la última versión de MariaDB 10.4 en uno de nuestros esclavos MySQL 8.0 (esclavo2):

(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

La utilidad mysqlbinlog de MariaDB ahora se encuentra en /root/mysqlbinlog en slave2. En el maestro MySQL, ejecutamos la siguiente declaración desastrosa:

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

Se eliminaron 96 filas en la declaración anterior. Espere un par de segundos para permitir que los eventos se repliquen del maestro a todos los esclavos antes de que podamos intentar encontrar la posición binlog del evento desastroso en el servidor esclavo. El primer paso es recuperar todos los registros binarios en ese servidor:

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

Nuestro evento desastroso debería existir dentro de binlog.000002, el registro binario más reciente en este servidor. Luego podemos usar la utilidad mysqlbinlog de MariaDB para recuperar todos los eventos binlog para la tabla sbtest1 desde hace 10 minutos:

(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

Para buscar fácilmente el número de posición del binlog, preste atención a las líneas que comienzan con "# at". De las líneas anteriores, podemos ver que el evento DELETE estaba ocurriendo en la posición 281 dentro de binlog.000002 (comienza en "# en 281"). También podemos recuperar los eventos binlog directamente dentro de un servidor 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)

Ahora podemos confirmar que la posición 281 es donde queremos que se reviertan nuestros datos. Luego podemos usar el indicador --start-position para generar eventos de flashback precisos. Tenga en cuenta que omitimos el indicador "-vv" y agregue la marca --flashback:

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

El flashback.binlog contiene todos los eventos necesarios para deshacer todos los cambios ocurridos en la tabla sbtest1 en este servidor MySQL. Dado que este es un nodo esclavo de un clúster de replicación, tenemos que interrumpir la replicación en el esclavo elegido (esclavo2) para usarlo con fines de flashback. Para hacer esto, tenemos que detener la replicación en el esclavo elegido, establecer MySQL GTID en ON_PERMISSIVE y hacer que el esclavo sea escribible:

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

En este punto, slave2 no es parte de la replicación y nuestra topología se ve así:

Importe el flashback a través del cliente mysql y no queremos que este cambio sea registrado en el registro binario de MySQL:

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

Entonces podemos ver todas las filas eliminadas, como lo demuestra la siguiente declaración:

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

Luego podemos crear un archivo de volcado de SQL para la tabla sbtest1 para nuestra referencia:

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

Una vez que se completa la operación de flashback, podemos volver a unir el nodo esclavo a la cadena de replicación. Pero primero, tenemos que devolver la base de datos a un estado consistente, reproduciendo todos los eventos a partir de la posición que habíamos retrocedido. No olvide omitir el registro binario ya que no queremos "escribir" en el esclavo y arriesgarnos con transacciones erróneas:

(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

Finalmente, prepare el nodo para volver a su rol como esclavo de MySQL e inicie la replicación:

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

Verifique que el nodo esclavo se esté replicando correctamente:

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

En este punto, hemos vuelto a unir el esclavo a la cadena de replicación y nuestra topología ahora vuelve a su estado original:

¡Gracias al equipo de MariaDB por presentar esta característica asombrosa!