Recientemente nos encontramos con un caso interesante de atención al cliente relacionado con una configuración de replicación de MariaDB. Pasamos mucho tiempo investigando este problema y pensamos que valdría la pena compartirlo contigo en esta publicación de blog.
Descripción del entorno del cliente
El problema era el siguiente:se estaba usando un servidor MariaDB antiguo (anterior a 10.x) y se intentó migrar datos desde él a una configuración de replicación MariaDB más reciente. Esto resultó en problemas con el uso de Mariabackup para reconstruir esclavos en el nuevo clúster de replicación. A los efectos de las pruebas, recreamos este comportamiento en el siguiente entorno:
Los datos se han migrado de 5.5 a 10.4 usando mysqldump:
mysqldump --single-transaction --master-data=2 --events --routines sbtest > /root/dump.sql
Esto nos permitió recopilar las coordenadas del registro binario maestro y el volcado consistente. Como resultado, pudimos aprovisionar el nodo maestro MariaDB 10.4 y configurar la replicación entre el antiguo maestro 5.5 y el nuevo nodo 10.4. El tráfico aún se estaba ejecutando en el nodo 5.5. El maestro 10.4 generaba GTID, ya que tenía que replicar los datos en el esclavo 10.4. Antes de profundizar en los detalles, echemos un vistazo rápido a cómo funciona GTID en MariaDB.
MariaDB y GTID
Para empezar, MariaDB usa un formato de GTID diferente al de Oracle MySQL. Consta de tres números separados por guiones:
0 - 1 - 345
Primero es un dominio de replicación, que permite que la replicación de varias fuentes se maneje correctamente. Esto no es relevante para nuestro caso ya que todos los nodos están en el mismo dominio de replicación. El segundo número es el ID del servidor del nodo que generó el GTID. El tercero es el número de secuencia:aumenta monótonamente con cada evento almacenado en los registros binarios.
MariaDB utiliza varias variables para almacenar la información sobre los GTID ejecutados en un nodo determinado. Los más interesantes para nosotros son:
Gtid_binlog_pos:según la documentación, esta variable es el GTID del último grupo de eventos escrito en el registro binario.
Gtid_slave_pos:según la documentación, esta variable del sistema contiene el GTID de la última transacción aplicada a la base de datos por los subprocesos esclavos del servidor.
Gtid_current_pos:según la documentación, esta variable del sistema contiene el GTID de la última transacción aplicada a la base de datos. Si el ID_servidor del GTID correspondiente en gtid_binlog_pos es igual al ID_servidor propio del servidor, y el número de secuencia es mayor que el GTID correspondiente en gtid_slave_pos, entonces se usará el GTID de gtid_binlog_pos. De lo contrario, se usará el GTID de gtid_slave_pos para ese dominio.
Entonces, para que quede claro, gtid_binlog_pos almacena el GTID del último evento ejecutado localmente. Gtid_slave_pos almacena el GTID del evento ejecutado por el subproceso esclavo y gtid_current_pos muestra el valor de gtid_binlog_pos, si tiene el número de secuencia más alto y tiene ID de servidor o gtid_slave_pos si tiene la secuencia más alta. Por favor, ten esto en cuenta.
Una descripción general del problema
El estado inicial de las variables relevantes está en el maestro 10.4:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| gtid_binlog_pos | 0-1001-1 |
| gtid_binlog_state | 0-1001-1 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-1 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+----------+
11 rows in set (0.001 sec)
Tenga en cuenta gtid_slave_pos que, en teoría, no tiene sentido:proviene del mismo nodo pero a través de un subproceso esclavo. Esto podría suceder si realiza un cambio maestro antes. Hicimos exactamente eso:con dos nodos 10.4, cambiamos los maestros del host con ID de servidor de 1001 a host con ID de servidor de 1002 y luego de regreso a 1001.
Después configuramos la replicación de 5.5 a 10.4 y así es como se veían las cosas:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+-------------------------+
| Variable_name | Value |
+-------------------------+-------------------------+
| gtid_binlog_pos | 0-55-117029 |
| gtid_binlog_state | 0-1001-1537,0-55-117029 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-1 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+-------------------------+
11 rows in set (0.000 sec)
Como puede ver, los eventos replicados desde MariaDB 5.5, todos se contabilizaron en la variable gtid_binlog_pos:todos los eventos con ID de servidor de 55. Esto resulta en un problema grave. Como recordará, gtid_binlog_pos debe contener eventos ejecutados localmente en el host. Aquí contiene eventos replicados desde otro servidor con diferente ID de servidor.
Esto complica las cosas cuando desea reconstruir el esclavo 10.4, este es el motivo. Mariabackup, al igual que Xtrabackup, funciona de forma sencilla. Copia los archivos del servidor MariaDB mientras escanea los registros de rehacer y almacena las transacciones entrantes. Cuando se hayan copiado los archivos, Mariabackup congelaría la base de datos utilizando FLUSH TABLES WITH READ LOCK o bloqueos de copia de seguridad, según la versión de MariaDB y la disponibilidad de los bloqueos de copia de seguridad. Luego lee el último GTID ejecutado y lo almacena junto con la copia de seguridad. Luego se libera el bloqueo y se completa la copia de seguridad. El GTID almacenado en la copia de seguridad debe usarse como el último GTID ejecutado en un nodo. En caso de reconstruir esclavos, se colocará como gtid_slave_pos y luego se usará para iniciar la replicación de GTID. Este GTID se toma de gtid_current_pos, lo que tiene perfecto sentido; después de todo, es el "GTID de la última transacción aplicada a la base de datos". El lector agudo ya puede ver el problema. Mostremos el resultado de las variables cuando 10.4 se replica desde el maestro 5.5:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+-------------------------+
| Variable_name | Value |
+-------------------------+-------------------------+
| gtid_binlog_pos | 0-55-117029 |
| gtid_binlog_state | 0-1001-1537,0-55-117029 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-1 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+-------------------------+
11 rows in set (0.000 sec)
Gtid_current_pos está establecido en 0-1001-1. Este definitivamente no es el momento correcto en el tiempo, se tomó de gtid_slave_pos mientras que tenemos un montón de transacciones que vinieron de 5.5 después de eso. El problema es que esas transacciones se almacenan como gtid_binlog_pos. Por otro lado, gtid_current_pos se calcula de manera que requiere la identificación del servidor local para los GTID en gitd_binlog_pos antes de que puedan usarse como gtid_current_pos. En nuestro caso, tienen la ID del servidor del nodo 5.5, por lo que no se tratarán correctamente como eventos ejecutados en el maestro 10.4. Después de la restauración de la copia de seguridad, si configurara el esclavo de acuerdo con el estado de GTID almacenado en la copia de seguridad, terminaría volviendo a aplicar todos los eventos que vinieron de 5.5. Esto, obviamente, rompería la replicación.
La solución
Una solución a este problema es tomar varios pasos adicionales:
- Detenga la replicación de 5.5 a 10.4. Ejecute STOP SLAVE en el maestro 10.4
- Ejecutar cualquier transacción en 10.4 - CREAR ESQUEMA SI NO EXISTE corrección de errores - esto cambiará la situación de GTID de esta manera:
MariaDB [(none)]> show global variables like '%gtid%';
+-------------------------+---------------------------+
| Variable_name | Value |
+-------------------------+---------------------------+
| gtid_binlog_pos | 0-1001-117122 |
| gtid_binlog_state | 0-55-117121,0-1001-117122 |
| gtid_cleanup_batch_size | 64 |
| gtid_current_pos | 0-1001-117122 |
| gtid_domain_id | 0 |
| gtid_ignore_duplicates | ON |
| gtid_pos_auto_engines | |
| gtid_slave_pos | 0-1001-1 |
| gtid_strict_mode | ON |
| wsrep_gtid_domain_id | 0 |
| wsrep_gtid_mode | OFF |
+-------------------------+---------------------------+
11 rows in set (0.001 sec)
El último GITD se ejecutó localmente, por lo que se almacenó como gtid_binlog_pos. Como tiene la ID del servidor local, se elige como gtid_current_pos. Ahora, puede realizar una copia de seguridad y usarla para reconstruir los esclavos del maestro 10.4. Una vez hecho esto, inicie el subproceso esclavo de nuevo.
MariaDB es consciente de que este tipo de error existe, uno de los informes de errores relevantes que encontramos es: https://jira.mariadb.org/browse/MDEV-10279 Desafortunadamente, no hay solución hasta el momento . Lo que encontramos es que este problema afecta a MariaDB hasta 5.5. Los eventos que no son GTID que provienen de MariaDB 10.0 se contabilizan correctamente en 10.4 como provenientes del subproceso esclavo y gtid_slave_pos se actualiza correctamente. MariaDB 5.5 es bastante antigua (aunque todavía es compatible), por lo que aún puede ver configuraciones ejecutándose e intentos de migrar de 5.5 a versiones más recientes de MariaDB habilitadas para GTID. Lo que es peor, según el informe de errores que encontramos, esto también afecta la replicación proveniente de servidores que no son de MariaDB (uno de los comentarios menciona el problema que aparece en Percona Server 5.6) en MariaDB.
De todos modos, esperamos que haya encontrado útil esta publicación de blog y esperamos que no se encuentre con el problema que acabamos de describir.