sql >> Base de Datos >  >> RDS >> Mysql

Replicación de MySQL y conmutación por error basada en GTID:una inmersión profunda en las transacciones erráticas

Durante años, la replicación de MySQL solía basarse en eventos de registro binario:todo lo que un esclavo sabía era el evento exacto y la posición exacta que acababa de leer del maestro. Cualquier transacción individual de un maestro puede haber terminado en diferentes registros binarios y en diferentes posiciones en estos registros. Era una solución simple que venía con limitaciones:los cambios de topología más complejos podrían requerir que un administrador detuviera la replicación en los hosts involucrados. O estos cambios podrían causar otros problemas, por ejemplo, un esclavo no se puede mover hacia abajo en la cadena de replicación sin un proceso de reconstrucción que consume mucho tiempo (no pudimos cambiar fácilmente la replicación de A -> B -> C a A -> C -> B sin detener la replicación tanto en B como en C). Todos hemos tenido que sortear estas limitaciones mientras soñamos con un identificador de transacción global.

GTID se introdujo junto con MySQL 5.6 y trajo algunos cambios importantes en la forma en que opera MySQL. En primer lugar, cada transacción tiene un identificador único que la identifica de la misma manera en todos los servidores. Ya no importa en qué posición del registro binario se registró una transacción, todo lo que necesita saber es el GTID:'966073f3-b6a4-11e4-af2c-080027880ca6:4'. GTID se construye a partir de dos partes:el identificador único de un servidor donde se ejecutó una transacción por primera vez y un número de secuencia. En el ejemplo anterior, podemos ver que la transacción fue ejecutada por el servidor con server_uuid de '966073f3-b6a4-11e4-af2c-080027880ca6' y es la cuarta transacción ejecutada allí. Esta información es suficiente para realizar cambios de topología complejos:MySQL sabe qué transacciones se han ejecutado y, por lo tanto, sabe qué transacciones deben ejecutarse a continuación. Olvídese de los registros binarios, todo está en el GTID.

Entonces, ¿dónde puedes encontrar los GTID? Los encontrarás en dos lugares. En un esclavo, en 'mostrar estado de esclavo', encontrará dos columnas:Retrieved_Gtid_Set y Executed_Gtid_Set. El primero cubre los GTID que se recuperaron del maestro a través de la replicación, el segundo informa sobre todas las transacciones que se ejecutaron en el host dado, tanto a través de la replicación como ejecutadas localmente.

Configuración de un clúster de replicación de forma fácil

La implementación del clúster de replicación de MySQL es muy fácil en ClusterControl (puede probarlo gratis). El único requisito previo es que se pueda acceder a todos los hosts, que usará para implementar los nodos de MySQL, desde la instancia de ClusterControl mediante una conexión SSH sin contraseña.

Cuando la conectividad está en su lugar, puede implementar un clúster mediante la opción "Implementar". Cuando la ventana del asistente está abierta, debe tomar un par de decisiones:¿qué desea hacer? ¿Implementar un nuevo clúster? Implemente un nodo de Postgresql o importe un clúster existente.

Queremos implementar un nuevo clúster. Luego se nos presentará la siguiente pantalla en la que debemos decidir qué tipo de clúster queremos implementar. Elijamos la replicación y luego pasemos los detalles requeridos sobre la conectividad ssh.

Cuando esté listo, haga clic en Continuar. Esta vez tenemos que decidir qué proveedor de MySQL nos gustaría usar, qué versión y un par de ajustes de configuración que incluyen, entre otros, la contraseña de la cuenta raíz en MySQL.

Finalmente, tenemos que decidir sobre la topología de replicación:puede usar una configuración típica maestro-esclavo o crear un par más complejo, activo-maestro en espera-maestro (+ esclavos si desea agregarlos). Una vez que esté listo, simplemente haga clic en "Implementar" y en un par de minutos debería tener su clúster implementado.

Una vez hecho esto, verá su clúster en la lista de clústeres de la interfaz de usuario de ClusterControl.

Con la replicación en funcionamiento, podemos ver más de cerca cómo funciona GTID.

Transacciones erróneas:¿cuál es el problema?

Como mencionamos al comienzo de esta publicación, los GTID trajeron un cambio significativo en la forma en que las personas deberían pensar sobre la replicación de MySQL. Se trata de hábitos. Digamos, por alguna razón, que una aplicación realizó una escritura en uno de los esclavos. No debería haber sucedido, pero sorprendentemente, sucede todo el tiempo. Como resultado, la replicación se detiene con un error de clave duplicada. Hay un par de maneras de lidiar con este problema. Uno de ellos sería eliminar la fila infractora y reiniciar la replicación. Otro sería omitir el evento de registro binario y luego reiniciar la replicación.

STOP SLAVE SQL_THREAD; SET GLOBAL sql_slave_skip_counter = 1; START SLAVE SQL_THREAD;

Ambas formas deberían hacer que la replicación vuelva a funcionar, pero pueden introducir una deriva de datos, por lo que es necesario recordar que la coherencia del esclavo debe verificarse después de tal evento (pt-table-checksum y pt-table-sync funcionan bien aquí).

Si ocurre un problema similar al usar GTID, notará algunas diferencias. Eliminar la fila infractora puede parecer que soluciona el problema, la replicación debería poder comenzar. El otro método, usar sql_slave_skip_counter no funcionará en absoluto, devolverá un error. Recuerde, ahora no se trata de eventos binlog, se trata de que GTID se ejecute o no.

¿Por qué eliminar la fila solo "parece" solucionar el problema? Una de las cosas más importantes a tener en cuenta con respecto a GTID es que un esclavo, cuando se conecta al maestro, verifica si falta alguna transacción que se haya ejecutado en el maestro. Estas se llaman transacciones errantes. Si un esclavo encuentra tales transacciones, las ejecutará. Supongamos que ejecutamos el siguiente SQL para borrar una fila infractora:

DELETE FROM mytable WHERE id=100;

Verifiquemos mostrar el estado del esclavo:

                  Master_UUID: 966073f3-b6a4-11e4-af2c-080027880ca6
           Retrieved_Gtid_Set: 966073f3-b6a4-11e4-af2c-080027880ca6:1-29
            Executed_Gtid_Set: 84d15910-b6a4-11e4-af2c-080027880ca6:1,
966073f3-b6a4-11e4-af2c-080027880ca6:1-29,

Y mira de dónde viene el 84d15910-b6a4-11e4-af2c-080027880ca6:1:

mysql> SHOW VARIABLES LIKE 'server_uuid'\G
*************************** 1. row ***************************
Variable_name: server_uuid
        Value: 84d15910-b6a4-11e4-af2c-080027880ca6
1 row in set (0.00 sec)

Como puede ver, tenemos 29 transacciones que provienen del maestro, UUID de 966073f3-b6a4-11e4-af2c-080027880ca6 y una que se ejecutó localmente. Digamos que en algún momento hacemos failover y el maestro (966073f3-b6a4-11e4-af2c-080027880ca6) se convierte en esclavo. Verificará su lista de GTID ejecutados y no encontrará este:84d15910-b6a4-11e4-af2c-080027880ca6:1. Como resultado, se ejecutará el SQL relacionado:

DELETE FROM mytable WHERE id=100;

Esto no es algo que esperábamos... Si, mientras tanto, el binlog que contiene esta transacción se purga en el antiguo esclavo, entonces el nuevo esclavo se quejará después de la conmutación por error:

                Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'

¿Cómo detectar transacciones errantes?

MySQL proporciona dos funciones que son muy útiles cuando desea comparar conjuntos de GTID en diferentes hosts.

GTID_SUBSET() toma dos conjuntos de GTID y verifica si el primer conjunto es un subconjunto del segundo.

Digamos que tenemos el siguiente estado.

Maestro:

mysql> show master status\G
*************************** 1. row ***************************
             File: binlog.000002
         Position: 160205927
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set: 8a6962d2-b907-11e4-bebc-080027880ca6:1-153,
9b09b44a-b907-11e4-bebd-080027880ca6:1,
ab8f5793-b907-11e4-bebd-080027880ca6:1-2
1 row in set (0.00 sec)

Esclavo:

mysql> show slave status\G
[...]
           Retrieved_Gtid_Set: 8a6962d2-b907-11e4-bebc-080027880ca6:1-153,
9b09b44a-b907-11e4-bebd-080027880ca6:1
            Executed_Gtid_Set: 8a6962d2-b907-11e4-bebc-080027880ca6:1-153,
9b09b44a-b907-11e4-bebd-080027880ca6:1,
ab8f5793-b907-11e4-bebd-080027880ca6:1-4

Podemos verificar si el esclavo tiene transacciones erráticas ejecutando el siguiente SQL:

mysql> SELECT GTID_SUBSET('8a6962d2-b907-11e4-bebc-080027880ca6:1-153,ab8f5793-b907-11e4-bebd-080027880ca6:1-4', '8a6962d2-b907-11e4-bebc-080027880ca6:1-153, 9b09b44a-b907-11e4-bebd-080027880ca6:1, ab8f5793-b907-11e4-bebd-080027880ca6:1-2') as is_subset\G
*************************** 1. row ***************************
is_subset: 0
1 row in set (0.00 sec)

Parece que hay transacciones errantes. ¿Cómo los identificamos? Podemos usar otra función, GTID_SUBTRACT()

mysql> SELECT GTID_SUBTRACT('8a6962d2-b907-11e4-bebc-080027880ca6:1-153,ab8f5793-b907-11e4-bebd-080027880ca6:1-4', '8a6962d2-b907-11e4-bebc-080027880ca6:1-153, 9b09b44a-b907-11e4-bebd-080027880ca6:1, ab8f5793-b907-11e4-bebd-080027880ca6:1-2') as mising\G
*************************** 1. row ***************************
mising: ab8f5793-b907-11e4-bebd-080027880ca6:3-4
1 row in set (0.01 sec)

Nuestros GTID faltantes son ab8f5793-b907-11e4-bebd-080027880ca6:3-4 - esas transacciones se ejecutaron en el esclavo pero no en el maestro.

¿Cómo resolver problemas causados ​​por transacciones erróneas?

Hay dos formas:inyectar transacciones vacías o excluir transacciones del historial de GTID.

Para inyectar transacciones vacías podemos usar el siguiente SQL:

mysql> SET gtid_next='ab8f5793-b907-11e4-bebd-080027880ca6:3';
Query OK, 0 rows affected (0.01 sec)
mysql> begin ; commit;
Query OK, 0 rows affected (0.00 sec)
  
Query OK, 0 rows affected (0.01 sec)
mysql> SET gtid_next='ab8f5793-b907-11e4-bebd-080027880ca6:4';
Query OK, 0 rows affected (0.00 sec)
mysql> begin ; commit;
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.01 sec)
mysql> SET gtid_next=automatic;
Query OK, 0 rows affected (0.00 sec)

Esto debe ejecutarse en cada host en la topología de replicación que no tenga esos GTID ejecutados. Si el maestro está disponible, puede inyectar esas transacciones allí y dejar que se repliquen en la cadena. Si el maestro no está disponible (por ejemplo, se bloqueó), esas transacciones vacías deben ejecutarse en cada esclavo. Oracle desarrolló una herramienta llamada mysqlslavetrx que está diseñada para automatizar este proceso.

Otro enfoque es eliminar los GTID del historial:

Detener esclavo:

mysql> STOP SLAVE;

Imprimir Executed_Gtid_Set en el esclavo:

mysql> SHOW MASTER STATUS\G

Restablecer información de GTID:

RESET MASTER;

Establezca GTID_PURGED en un conjunto de GTID correcto. basado en datos de SHOW MASTER STATUS. Debe excluir las transacciones erróneas del conjunto.

SET GLOBAL GTID_PURGED='8a6962d2-b907-11e4-bebc-080027880ca6:1-153, 9b09b44a-b907-11e4-bebd-080027880ca6:1, ab8f5793-b907-11e4-bebd-080027880ca6:1-2';

Iniciar esclavo.

mysql> START SLAVE\G

En todos los casos, debe verificar la consistencia de sus esclavos utilizando pt-table-checksum y pt-table-sync (si es necesario); la transacción errada puede resultar en una desviación de datos.

Conmutación por error en ClusterControl

A partir de la versión 1.4, ClusterControl mejoró sus procesos de manejo de conmutación por error para la replicación de MySQL. Todavía puede realizar un cambio de maestro manual promoviendo uno de los esclavos a maestro. El resto de los esclavos se conmutarán por error al nuevo maestro. A partir de la versión 1.4, ClusterControl también tiene la capacidad de realizar una conmutación por error completamente automatizada en caso de que falle el maestro. Lo cubrimos en profundidad en una publicación de blog que describe ClusterControl y la conmutación por error automatizada. Todavía nos gustaría mencionar una función, directamente relacionada con el tema de esta publicación.

De forma predeterminada, ClusterControl realiza la conmutación por error de una "manera segura":en el momento de la conmutación por error (o conmutación, si es el usuario quien ejecutó un cambio maestro), ClusterControl elige un candidato maestro y luego verifica que este nodo no tenga transacciones erráticas. lo que afectaría la replicación una vez que se promueva a maestro. Si se detecta una transacción errónea, ClusterControl detendrá el proceso de conmutación por error y el candidato maestro no será promovido para convertirse en un nuevo maestro.

Si desea estar 100 % seguro de que ClusterControl promoverá un nuevo maestro incluso si se detectan algunos problemas (como transacciones erráticas), puede hacerlo usando la configuración replication_stop_on_error=0 en la configuración de cmon. Por supuesto, como comentamos, puede generar problemas con la replicación:los esclavos pueden comenzar a solicitar un evento de registro binario que ya no está disponible.

Para manejar tales casos, agregamos soporte experimental para la reconstrucción de esclavos. Si configura replication_auto_rebuild_slave=1 en la configuración de cmon y su esclavo está marcado como inactivo con el siguiente error en MySQL, ClusterControl intentará reconstruir el esclavo utilizando los datos del maestro:

Obtuve el error fatal 1236 del maestro al leer datos del registro binario:"El esclavo se está conectando usando CHANGE MASTER TO MASTER_AUTO_POSITION =1, pero el maestro ha purgado los registros binarios que contienen los GTID que requiere el esclavo".

Tal configuración puede no ser siempre apropiada ya que el proceso de reconstrucción inducirá una mayor carga en el maestro. También puede ser que su conjunto de datos sea muy grande y una reconstrucción regular no sea una opción, por eso este comportamiento está deshabilitado de manera predeterminada.