El failover es donde los buenos diseños de replicación son juzgados. Todo luce limpio en los diagramas: un primario, un réplica, un VIP, quizá una capa de proxy y un runbook que nadie lee hasta que son las 3 a.m. Entonces un disco se queda, ocurre un fallo de red y de repente el “nuevo primario” rechaza escrituras, las réplicas apuntan al host equivocado o, peor: dos primarios aceptan tráfico y tu negocio inventa un nuevo tipo de contabilidad.
MySQL y MariaDB ambos “hacen replicación”. No hacen failover de la misma forma en la vida real. Las diferencias rara vez se tratan de características principales y casi siempre de comportamientos marginales: semántica GTID, compatibilidad de binlog, tablas de metadatos, suposiciones de proxies y el tipo de problemas operativos que solo aparecen bajo estrés.
Verdad fundamental: replicación no es failover
La replicación mueve cambios de datos de A a B. El failover mueve la autoridad de A a B, además del tráfico y de los rieles de seguridad. Esos rieles son donde están los cadáveres.
En producción, el failover tiene tres tareas:
- Elegir el candidato correcto (el más cercano a los datos actuales, más saludable, no en recuperación de un fallo).
- Hacerlo escribible (y asegurar que el antiguo primario no pueda aceptar escrituras, aunque vuelva enfadado).
- Mover los clientes (proxies, DNS, VIPs, pools de conexiones, cachés de la aplicación y la lógica de reintentos “útil”).
MySQL y MariaDB ambos soportan replicación asíncrona. Ambos pueden usar GTIDs (con diferencias importantes). Ambos pueden tener sabores semi-sync. Ambos pueden ser gestionados por orquestadores o proxies. Y ambos te pueden traicionar si tratas la replicación como una varita mágica en lugar de como un contrato.
Una verdad operativa que vale la pena imprimir en una pegatina: el failover es una decisión de consistencia. Siempre estás intercambiando entre disponibilidad ahora y corrección de datos después. Tus herramientas deberían forzar que esa decisión sea explícita, no implícita.
Broma #1: El retraso de replicación es solo tu base de datos practicando mindfulness: vive en el pasado para no preocuparse por el futuro.
Antes de comparar MySQL y MariaDB, fijemos el vocabulario tal y como lo usan los SREs:
- Promoción: convertir una réplica en un primario escribible.
- Reapuntar: cambiar las réplicas para que sigan al nuevo primario.
- Fencing: impedir que el antiguo primario acepte escrituras (apagado, aislamiento de red, STONITH, fencing de almacenamiento).
- Cerebros divididos (split brain): dos nodos aceptan escrituras para el mismo conjunto de datos. Esto no es “un incidente”, es “una nueva era de tus datos”.
- Herramienta de failover: Orchestrator, MHA, MaxScale, scripts de ProxySQL, Pacemaker/Corosync, operadores de Kubernetes o tu propio bash que finges que es “temporal”.
Hechos e historia interesantes que siguen importando
Esto no son datos para trivia. Explican por qué el failover de replicación MySQL y MariaDB difiere en formas sutiles y operativas.
- MariaDB se bifurcó de MySQL en 2009 tras la trayectoria de adquisición de MySQL (Sun, luego Oracle). La divergencia empezó despacio y luego se aceleró, especialmente alrededor de GTID y características de replicación.
- MySQL 5.6 introdujo GTIDs como la primera funcionalidad “global transaction ID” de Oracle MySQL; MariaDB implementó un formato y comportamiento de GTID diferente.
- La replicación multihilo de MySQL maduró más tarde (notablemente 5.7+ con mejoras de replicación paralela, luego mejoras en 8.0). MariaDB siguió diferentes controles y implementaciones; las recetas de tuning no se transfieren limpiamente.
- MySQL 5.7 hizo InnoDB el predeterminado y mejoró la fiabilidad alrededor de recuperación tras fallos y metadatos de replicación. MariaDB también mejoró InnoDB (y variantes como XtraDB históricamente), pero los síntomas operativos difieren.
- MySQL 8.0 cambió valores por defecto y eliminó comportamientos antiguos (como la caché de consultas ya inexistente, pero más importante, plugins de autenticación y comportamiento de metadatos). El failover entre versiones mixtas puede fallar por razones que parecen no relacionadas con la replicación.
- MariaDB introdujo “domain IDs” en los GTIDs que pueden ser útiles (multi-source) y confusos (mala gestión de dominios durante el failover).
- Group Replication/InnoDB Cluster de MySQL es una vía HA de primera clase en Oracle MySQL; MariaDB tiene Galera y su propia historia de HA. La gente compara replicación asíncrona con failover mientras secretamente quiere un sistema de consenso.
- Ambos ecosistemas desarrollaron herramientas operativas por separado: Orchestrator nació en el mundo MySQL pero puede funcionar con MariaDB; MaxScale es el proxy de MariaDB con comportamientos orientados a MariaDB. Mezclar herramientas y asumir paridad de funciones es un patrón común de fallo.
Qué falla durante el failover (MySQL vs MariaDB)
1) Failover con GTID: “Usamos GTID, así que está seguro”… hasta que no
El failover basado en GTID debería simplificar la promoción: elegir la réplica más actualizada, promoverla y reapuntar a las demás usando auto-posicionamiento GTID. En la práctica, GTID no significa “sin pérdida de datos”, significa “mejor contabilidad”.
El comportamiento GTID en MySQL suele girar alrededor de gtid_executed, gtid_purged y conjuntos GTID. Las herramientas de failover suelen usar comparaciones de conjuntos GTID de MySQL para encontrar la réplica más avanzada.
El comportamiento GTID en MariaDB usa un formato de GTID distinto e incluye conceptos como domain IDs y números de secuencia. Es potente, pero también hace que “simplemente comparar GTIDs” sea menos uniforme en flotas mixtas.
Qué falla:
- Incompatibilidades de estrictidad GTID: una réplica que no puede aceptar ciertos GTIDs después de la promoción debido a huecos, historial purgado o diferencias de configuración.
- Herramientas que asumen variables GTID al estilo MySQL y fallan en MariaDB (o las interpretan incorrectamente).
- Failover entre versiones: la “mejor” réplica está en una versión ligeramente distinta con valores por defecto diferentes; la promoción tiene éxito, los clientes fallan.
2) Metadatos de replicación que se vuelven raros bajo estrés
Durante el failover haces muchas transiciones de estado rápidamente: detener replicación, resetear información de replicación, cambiar primario, iniciar replicación y a veces reconstruir relay logs. Los detalles difieren entre MySQL y MariaDB, incluyendo sintaxis de comandos y campos de estado.
En MySQL 8.0, los comandos de replicación cambiaron (START REPLICA en lugar de START SLAVE), los nombres de estado se desplazaron (SHOW REPLICA STATUS) y los metadatos se movieron más firmemente a tablas transaccionales. MariaDB mantuvo la nomenclatura tradicional por más tiempo y tiene su propio comportamiento respecto a relay logs y GTIDs.
Qué falla:
- Runbooks escritos para una sintaxis que se ejecutan contra la otra a las 3 a.m. y silenciosamente no hacen nada.
- Tablas de información de replicación inconsistentes después de comandos parciales (especialmente cuando la automatización expira en medio del cambio).
- Corrupción de relay logs tras un fallo, y tu “promoción simple” se convierte en una reconstrucción.
3) Semi-sync: latencia y controles de seguridad que no coinciden con tu modelo mental
La replicación semi-sync reduce la “brecha de ack” al requerir que al menos una réplica confirme la recepción antes de que el commit retorne. Eso suena a durabilidad. No es lo mismo que durabilidad ante fallos a menos que entiendas la semántica de ack y los timeouts.
El plugin semi-sync de MySQL y la implementación semi-sync de MariaDB se comportan de forma similar en espíritu, pero operativamente verás contadores de estado diferentes y firmas de fallo distintas. El riesgo es el mismo: cuando semi-sync cae a async durante un evento turbulento, tu RPO puede cambiar silenciosamente justo cuando más lo necesitas.
Qué falla:
- El failover asume “sin pérdida de datos” porque semi-sync estaba habilitado, pero ya había degradado a async por timeouts.
- Timeouts demasiado agresivos causan retrocesos frecuentes, aumentando la jitter de latencia de commits y provocando reintentos de la app que amplifican la carga.
4) Proxies y VIPs: la base de datos puede estar bien, pero el tráfico no
La mayoría de los fallos de failover fallan en la capa cliente. Los proxies cachean estado de backends, las apps cachean DNS, los pools de conexiones fijan sesiones y algunos drivers se comportan como niños cuando su endpoint favorito desaparece.
Las diferencias MySQL vs MariaDB aparecen cuando usas herramientas del ecosistema:
- MaxScale (el proxy de MariaDB) puede hacer failover dirigido por monitorización con supuestos específicos de MariaDB. Puede funcionar con MySQL, pero revisa límites de soporte con cuidado.
- Orchestrator (común en flotas MySQL) es excelente pero tiene opiniones. El soporte de MariaDB existe, sin embargo características como interpretación de GTID pueden diferir según versiones.
- ProxySQL es más agnóstico respecto a la base de datos pero depende de health checks y reglas de consultas; debes definir correctamente “writer” y “reader” y mantenerlo sincronizado con la lógica de promoción.
Qué falla: promocionas correctamente, pero el proxy sigue dirigiendo escrituras al antiguo primario por un minuto. Felicidades, acabas de construir un split brain con pasos extra.
5) No determinismo y los fantasmas de la replicación por sentencias
Si todavía usas replicación basada en sentencias (SBR), te estás escogiendo dolor. La replicación basada en filas (RBR) es el predeterminado moderno por una razón: evita comportamiento no determinista que convierte el failover en un ejercicio forense.
Algunas implantaciones de MariaDB mantienen formatos mixtos por razones legacy. Algunas de MySQL también, habitualmente tras una migración larga donde “temporal” se volvió permanente.
Qué falla:
- Una réplica aplica una sentencia de forma distinta por zona horaria, collation, SQL mode o diferencias en funciones entre versiones.
- El failover revela deriva oculta: el nodo promovido tiene datos que son sutilmente diferentes, no obviamente corruptos.
6) “read_only” no es fencing
MySQL y MariaDB soportan read_only y super_read_only (disponibilidad depende de la versión). Pero estos son controles advertenciales, no un cercado duro. Usuarios con privilegios suficientes aún pueden escribir en muchos escenarios (especialmente sin super_read_only), y tareas en segundo plano también pueden escribir.
Los planes de failover que dependen de poner read_only=ON en el antiguo primario son planes que asumen que el viejo primario cooperará. Los antiguos primarios no cooperan. Se enfadan y luego aceptan escrituras en cuanto se cuela una conexión.
7) Backup/restauración durante el re-seed: GTID y ajustes de binlog importan
Tras un failover, a menudo necesitas reconstruir una réplica. Si tu herramienta de backup no preserva el estado GTID correctamente, o si restauras con los ajustes de binlog incorrectos, terminas con una réplica que no puede unirse limpiamente.
La divergencia MySQL vs MariaDB aparece en el manejo del estado GTID, además de diferencias en la configuración por defecto y variables de replicación. Backups “compatibles con MySQL” no siempre son “compatibles con el posicionamiento GTID de failover de MariaDB”, especialmente si cruzas versiones.
Realidad GTID: mismo acrónimo, contrato distinto
La gente dice “usamos GTID” como si fuera una pregunta sí/no. No lo es. Es “usamos GTID, con estas restricciones, y sabemos qué sucede cuando esas restricciones se rompen”.
GTID en MySQL: conjuntos y auto-posicionamiento
GTID de MySQL suele gestionarse con:
gtid_mode=ONenforce_gtid_consistency=ONlog_slave_updates=ON(para que las réplicas puedan convertirse en primarios)MASTER_AUTO_POSITION=1(auto-posicionamiento)
Las herramientas de failover gustan de GTID en MySQL porque pueden comparar conjuntos GTID. Pero hay una trampa: los conjuntos GTID solo te dicen lo que se ejecutó, no lo que tus clientes creen que se confirmó si el antiguo primario murió a mitad de camino. Semi-sync ayuda, pero puede degradarse.
GTID en MariaDB: domain IDs y aristas operativas
GTID de MariaDB es distinto. Los domain IDs pueden representar diferentes dominios de replicación. Eso puede ser útil para replicación multi-source y para rastrear streams. También puede introducir modos de fallo donde un nodo promovido tiene un estado GTID que parece “completo” pero no coincide con lo que otras réplicas esperan.
MariaDB también tiene ajustes como modo GTID estricto (dependiendo de la versión) que cambia si se toleran huecos. Eso es bueno para corrección, pero puede causar que promociones fallen de forma dura justo cuando quieres un aterrizaje suave.
Qué hacer con este conocimiento
Si eres homogéneo (todo MySQL o todo MariaDB), elige la vía GTID nativa y comprométete con ella. No trates a GTID como una casilla de migración. Trátalo como un diseño: método de backup, reconstrucción de réplica, procedimiento de promoción y monitorización deben coincidir.
Si eres mixto (MySQL y MariaDB), deja de fingir que el failover de replicación es simétrico. O desacoplas la herramienta de promoción por motor, o estandarizas un motor para el rol escritor. La replicación entre motores puede funcionar para algunos casos, pero el failover es donde aparecen primero las incompatibilidades.
Elecciones de topología que cambian tu radio de impacto
Primario-réplica asíncrono con failover manual: la base honesta
Este es el sistema más simple que puede funcionar. También fuerza a los humanos a entender los tradeoffs porque un humano pulsa el botón.
Pros: fácil de razonar; menos piezas móviles. Contras: recuperación más lenta; errores humanos; procedimiento inconsistente salvo que se practique.
Asíncrono con failover orquestado: automatización que puede hacer más daño más rápido
El failover orquestado es genial cuando está diseñado con fencing y selección cuidadosa de candidatos. Es peligroso cuando es “el monitor ve primario caído → promueve algo”. El monitor no es tu capa de corrección de datos.
La selección de candidatos debería considerar:
- Lag de replicación y lag de aplicación (no solo que el hilo IO esté en marcha)
- Transacciones errantes (especialmente con GTID)
- Salud del servidor (disco lleno, recuperación tras fallo, errores de filesystem)
- Alcance de red desde los clientes (un nodo promovido en una subred muerta es una caída muy rápida)
Sueños “multi-primario”: Group Replication, Galera y controles de realidad
Si realmente necesitas failover automático sin pérdida de datos, probablemente buscas un sistema basado en consenso. En el mundo MySQL, eso suele ser Group Replication/InnoDB Cluster. En MariaDB, a menudo son soluciones basadas en Galera.
Pero: esos no son “replicación con failover”, son bestias distintas con modos de fallo, características de rendimiento y requisitos operativos diferentes.
La replicación asíncrona con failover puede ser perfectamente aceptable si aceptas RPO>0 y tienes buen fencing. También puede ser una jugada que limite tu carrera si finges que es consistencia fuerte.
Guion de diagnóstico rápido
Cuando el failover va mal, necesitas encontrar el cuello de botella rápidamente. No el cuello filosófico. La cosa real que impide restaurar el servicio de escritura con seguridad.
Primero: establece autoridad y evita split brain
- ¿Quién es actualmente escribible? Revisa
read_only/super_read_onlyy si los clientes están escribiendo con éxito. - Fencia el antiguo primario (apagar, aislar red, deshabilitar VIP, bloquear en el proxy). Si no puedes fencearlo, no tienes failover; tienes una apuesta.
- Detén bucles de automatización que puedan re-promover, reparentar o voltear VIPs de un lado a otro.
Segundo: encuentra el candidato más correcto
- Compara posiciones GTID (o binlog file/pos si no hay GTID). Identifica la(s) réplica(s) más cercanas al antiguo primario.
- Revisa el estado del hilo SQL: ¿está aplicando? ¿stalled? ¿error? ¿esperando un metadata lock?
- Revisa la salud de InnoDB: recuperación tras fallo en curso, backlog de flush de páginas sucias, saturación IO.
Tercero: mueve tráfico sin mentirle a la app
- Actualiza proxies/VIP/DNS y verifica desde múltiples subredes de la app.
- Confirma separación escritor/lector en proxies; asegúrate de que las escrituras realmente lleguen al nuevo primario.
- Observa tasas de error y latencia. Un failover “exitoso” que causa un pico de latencia 10x sigue siendo una interrupción—solo más silenciosa.
Cuarto: reparar el set de réplicas
- Reapunta réplicas al nuevo primario y valida que repliquen limpiamente.
- Reconstruye réplicas que tengan corrupción de relay logs o transacciones errantes.
- Confirma que backups y retención de binlog son suficientes para futuras reconstrucciones.
Tareas prácticas: comandos, salidas y la decisión que tomas
Estas son las tareas que realmente ejecutas durante un failover desordenado. Cada una incluye: el comando, qué significa una salida típica y la decisión que tomas a partir de ello. Los comandos se muestran como si tuvieras acceso a shell y conectividad con el cliente MySQL.
Tarea 1: Identificar motor/versión rápidamente (porque sintaxis y variables difieren)
cr0x@server:~$ mysql -N -e "SELECT VERSION(), @@version_comment;"
10.6.18-MariaDB-log MariaDB Server
Qué significa: Estás en MariaDB 10.6. No pegues comandos de replicación de MySQL 8.0 sin pensar.
Decisión: Usa campos de estado de MariaDB (SHOW SLAVE STATUS) y variables GTID de MariaDB. Ajusta la rama del runbook.
Tarea 2: Comprobar si el servidor es actualmente escribible
cr0x@server:~$ mysql -N -e "SELECT @@read_only, @@super_read_only;"
0 0
Qué significa: Este nodo puede aceptar escrituras.
Decisión: Si este no es el primario previsto durante un incidente, debes fencearlo o ponerlo en modo solo-lectura inmediatamente (y también arreglar el enrutamiento de tráfico).
Tarea 3: Confirmar hilos de replicación y lag (nombres en MySQL 8 mostrados)
cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep "Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_SQL_Error"
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 2
Last_SQL_Error:
Qué significa: La replicación está en marcha con un lag mínimo.
Decisión: Esta réplica es una buena candidata de promoción desde la perspectiva de lag. Aún verifica estado GTID y salud.
Tarea 4: Mismo chequeo en MariaDB (los nombres de campo difieren)
cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_SQL_Error"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_SQL_Error:
Qué significa: La réplica de MariaDB está al día (según lo que este métrico puede decir).
Decisión: Aún verifica que no mienta: revisa posiciones GTID/binlog y logs de error si el primario murió de forma no limpia.
Tarea 5: Comprobar estado de ejecución GTID (MySQL)
cr0x@server:~$ mysql -N -e "SELECT @@gtid_executed\G"
*************************** 1. row ***************************
@@gtid_executed: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-984321
Qué significa: Este nodo ha ejecutado GTIDs hasta 984321 para ese UUID de servidor.
Decisión: Compara entre réplicas; la que tenga el superset suele ser la mejor candidata (sujeta a transacciones errantes y salud).
Tarea 6: Comprobar estado GTID (MariaDB)
cr0x@server:~$ mysql -N -e "SELECT @@gtid_current_pos, @@gtid_slave_pos;"
0-1-542118 0-1-542118
Qué significa: Dominio 0, server_id 1, secuencia 542118; la réplica ha aplicado hasta esa posición.
Decisión: Compara réplicas candidatas. Si ves múltiples domains inesperadamente, detente y mapea qué representan antes de promover.
Tarea 7: Detectar rotura de replicación por fila faltante (común tras ajustes inseguros)
cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Last_SQL_Errno|Last_SQL_Error"
Last_SQL_Errno: 1032
Last_SQL_Error: Could not execute Delete_rows event on table app.orders; Can't find record in 'orders', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND
Qué significa: Existe deriva de datos; la réplica no puede aplicar eventos limpiamente.
Decisión: No promociones esta réplica a menos que aceptes corrupción. Reconstrúyela desde un backup limpio o repara la deriva deliberadamente (enfoques tipo pt-table-checksum—lo que tu entorno permita).
Tarea 8: Confirmar formato de binlog y controles de durabilidad (preparación para promoción)
cr0x@server:~$ mysql -N -e "SELECT @@log_bin, @@binlog_format, @@sync_binlog, @@innodb_flush_log_at_trx_commit;"
1 ROW 1 1
Qué significa: Binary logging habilitado, basado en filas, y ajustes relativamente durables (sync_binlog=1, flush por commit).
Decisión: Bien. Si ves sync_binlog=0 o innodb_flush_log_at_trx_commit=2, debes asumir mayor riesgo de perder transacciones confirmadas en un crash—esto cambia tus expectativas de RPO para el failover.
Tarea 9: Comprobar si semi-sync está realmente activo (contadores del plugin en MySQL)
cr0x@server:~$ mysql -e "SHOW STATUS LIKE 'Rpl_semi_sync_master_status';"
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF |
+-----------------------------+-------+
Qué significa: Semi-sync no está activo actualmente, independientemente de la configuración. Probablemente retrocedió a async.
Decisión: Durante el failover, asume que puede haber transacciones faltantes en réplicas. Elige candidato basado en el estado aplicado real, no en la seguridad esperada.
Tarea 10: Validar que el “antiguo primario” está realmente fenceado
cr0x@server:~$ ssh db-old-primary "systemctl is-active mysql || systemctl is-active mariadb"
inactive
Qué significa: El servicio MySQL/MariaDB está detenido en el antiguo primario.
Decisión: Bien, pero no es suficiente si el host puede volver. Si tienes reinicios automáticos, desactívalos o aísla el nodo también a nivel de red.
Tarea 11: Confirmar a quién están realmente conectados los clientes (atrapa mentiras del proxy)
cr0x@server:~$ mysql -N -e "SELECT SUBSTRING_INDEX(USER(),'@',-1) AS client_host, COUNT(*) FROM information_schema.processlist GROUP BY 1 ORDER BY 2 DESC LIMIT 5;"
10.12.4.31 87
10.12.4.18 54
10.12.4.22 41
Qué significa: Muchas conexiones desde subredes de la app—este nodo recibe tráfico.
Decisión: Si promoviste este nodo, genial. Si no, necesitas arreglar el enrutamiento inmediatamente y considerar si ocurrieron escrituras aquí inesperadamente.
Tarea 12: En la réplica candidata, detener replicación limpiamente antes de la promoción
cr0x@server:~$ mysql -e "STOP REPLICA;"
Query OK, 0 rows affected (0.02 sec)
Qué significa: La replicación se detuvo (sintaxis MySQL 8).
Decisión: Ahora puedes evaluar el estado y promover sin que continúe aplicando eventos de una fuente potencialmente muerta.
Tarea 13: Hacer el nodo promovido escribible (y verificar)
cr0x@server:~$ mysql -e "SET GLOBAL super_read_only=OFF; SET GLOBAL read_only=OFF;"
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
cr0x@server:~$ mysql -N -e "SELECT @@read_only, @@super_read_only;"
0 0
Qué significa: El nodo es escribible.
Decisión: Haz esto solo después de fencear el antiguo primario. Si no puedes fencearlo, mantén el candidato en solo-lectura y cambia a una interrupción controlada mientras resuelves el fencing.
Tarea 14: Reapuntar otra réplica usando auto-posicionamiento GTID (MySQL)
cr0x@server:~$ mysql -e "STOP REPLICA; CHANGE REPLICATION SOURCE TO SOURCE_HOST='db-new-primary', SOURCE_USER='repl', SOURCE_PASSWORD='***', SOURCE_AUTO_POSITION=1; START REPLICA;"
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.04 sec)
Query OK, 0 rows affected (0.01 sec)
Qué significa: La réplica ahora sigue al nuevo primario con posicionamiento basado en GTID.
Decisión: Revisa inmediatamente el estado por errores y lag; si no alcanza, puedes tener transacciones errantes o GTIDs purgados.
Tarea 15: Reapuntar una réplica MariaDB (sintaxis clásica y GTID)
cr0x@server:~$ mysql -e "STOP SLAVE; CHANGE MASTER TO MASTER_HOST='db-new-primary', MASTER_USER='repl', MASTER_PASSWORD='***', MASTER_USE_GTID=slave_pos; START SLAVE;"
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.05 sec)
Query OK, 0 rows affected (0.01 sec)
Qué significa: Réplica de MariaDB reconfigurada para usar GTID empezando desde su propia posición aplicada.
Decisión: Si los errores de replicación mencionan estrictitud GTID o duplicados, detente y reconcilia posiciones GTID; no conviertas “contador skip” en un estilo de vida.
Tarea 16: Verificar que el nuevo primario tiene binlog habilitado (o las réplicas no podrán seguir)
cr0x@server:~$ mysql -N -e "SELECT @@log_bin;"
1
Qué significa: Binary logging está habilitado.
Decisión: Si esto devuelve 0, las réplicas no pueden replicar desde este nodo. O lo configuraste mal, o promoviste el tipo de host equivocado (un “perfil de solo lectura”). Arregla la configuración y reinicia, o elige otro candidato.
Tarea 17: Comprobar transacciones largas que bloquean la aplicación (a menudo confundido con “lag de replicación”)
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | egrep -n "TRANSACTIONS|LOCK WAIT|history list length" | head
3:TRANSACTIONS
45:---TRANSACTION 18446744073709551615, not started
112:History list length 987654
Qué significa: Una longitud muy grande de history list puede indicar lag de purge, transacciones de larga duración o carga de escritura intensa; a menudo se correlaciona con que la aplicación de replicación tenga problemas.
Decisión: Antes de promover una réplica con lag, entiende si está retrasada por saturación IO, lock waits o purge. La “más actualizada pero sobrecargada” puede caerse inmediatamente después de la promoción.
Tarea 18: Confirmar saturación de disco (el asesino silencioso del failover)
cr0x@server:~$ iostat -x 1 3
Linux 5.15.0 (db-replica-2) 12/29/2025 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.10 0.00 4.20 38.50 0.00 45.20
Device r/s w/s rkB/s wkB/s await aqu-sz %util
nvme0n1 120.0 980.0 5200.0 42000.0 35.4 18.2 99.8
Qué significa: El disco está al máximo (%util ~100), await alto. La aplicación de replicación y las escrituras de clientes sufrirán.
Decisión: No promociones este host si tienes otro candidato con IO más sano. Si es el único candidato, limita la carga, incrementa la seguridad del buffer pool y prepárate para una recuperación lenta.
Tres mini-historias corporativas desde la trinchera
Incidente causado por una suposición errónea: “read_only significa no escrituras”
Tenían una configuración estándar de dos nodos: un primario, una réplica y un proxy. El plan de failover era una sola página: si el primario muere, poner el antiguo primario en read_only=ON, promover la réplica y voltear el endpoint escritor del proxy.
El día llegó. Una actualización de kernel reinició el primario inesperadamente. La monitorización alertó, la automatización corrió y la réplica fue promovida. El proxy se volteó. Todos respiraron.
Luego el primario volvió. Systemd reinició el servicio de base de datos automáticamente. El proxy aún tenía un pool de conexiones obsoleto hacia el antiguo primario desde antes del failover y algunos nodos de aplicación tenían fallbacks codificados. Existía un usuario privilegiado para “mantenimiento”, con derechos suficientes para evitar el comportamiento de read_only de formas que el equipo no entendía. Las escrituras entraron al antiguo primario durante varios minutos.
Cuando luego reapuntaron ese antiguo primario como réplica, se negó: existían transacciones errantes. Ese es el modo de fallo educado. El modo descortés es cuando acepta replicación de todos modos y mandas basura silenciosamente.
La solución no fue “poner read_only más fuerte”. La solución fue fencing: o apagas el antiguo primario y lo mantienes apagado, o lo aíslas en la red para que no pueda aceptar tráfico cliente. También eliminaron hosts de fallback “útiles” de las configuraciones de la aplicación y convirtieron al proxy en la única fuente de verdad.
Una optimización que salió mal: “Hagamos los commits más rápidos”
Un equipo perseguía latencia. Tenían un servicio con muchas escrituras y picos de lag en replicación ocasionales. Alguien propuso bajar ajustes de durabilidad: innodb_flush_log_at_trx_commit=2 y sync_binlog=0. El argumento: “Podemos tolerar perder un segundo de datos; tenemos replicación de todos modos”.
Funcionó—hasta que no. Un primario se cayó durante un problema de almacenamiento. La base de datos arrancó limpia, pero la última ventana de transacciones “confirmadas” no estaba realmente durable en disco. Algunas transacciones fueron reconocidas a clientes pero faltaban del binlog y del estado redo de InnoDB.
El failover ocurrió al réplica más avanzada. La app volvió. Entonces el incidente se convirtió en un problema de cumplimiento porque sistemas downstream habían recibido confirmaciones por eventos que ahora no existían en la base de datos. El informe de errores parecía una teoría conspirativa, porque la aplicación tenía logs que decían “success” y la base de datos no tenía registro.
Al final restauraron la corrección reproduciendo eventos desde una cola externa y reconciliando. Nadie lo disfrutó. La optimización no solo aumentó el RPO; hizo los fallos no intuitivos. Ese es el costo real.
El compromiso eventual: mantener ajustes durables en el primario, usar mejor indexación y batching, e invertir en semi-sync (con monitorización para cuando caiga). El tuning que cambia la corrección debe tratarse como una decisión de producto, no como una ganancia rápida.
Una práctica aburrida pero correcta que salvó el día: “Ensayamos”
Otra compañía tenía lo que parecía exagerado: un simulacro de failover mensual, un runbook escrito con ramas específicas por motor y un pequeño script que hacía probes de lectura/escritura por la misma ruta que la aplicación.
Durante un incidente real (partición de red entre AZs), el primario quedó inaccesible desde la mayoría de las apps pero aún accesible desde algunas. Ese es el preludio de split-brain. La automatización se pausó porque los health checks discrepaban según los puntos de vista—por diseño.
El on-call siguió el playbook: fencear el primario a nivel de red, luego promover la mejor réplica basándose en estado GTID y salud de disco. El script de probe verificó que el “tráfico escritor” realmente alcanzara al nuevo primario, no solo que TCP estuviera abierto.
La interrupción no fue divertida, pero fue corta y controlada. Más tarde, en el postmortem, el logro del equipo no fue “cinco nueves”. Fue que nadie discutió sobre lo sucedido. La evidencia fue clara porque ensayaron los pasos de recolección de evidencia, no solo los de failover.
Ensayar es aburrido. También es una de las pocas cosas que convierte un diseño teórico de HA en uno práctico.
Errores comunes: síntomas → causa raíz → solución
1) Síntoma: la promoción “tiene éxito”, pero la aplicación sigue escribiendo al antiguo primario
Causa raíz: No hay fencing; el proxy aún enruta o las apps tienen listas de hosts de fallback; DNS/pools de conexión obsoletos.
Solución: Fencea el antiguo primario (apagado + aislamiento de red), haz del proxy el endpoint escritor único y valida con un probe de escritura a través de la ruta real de la app.
2) Síntoma: las réplicas no se conectan al nuevo primario tras la promoción
Causa raíz: El nodo promovido tiene log_bin=OFF, faltan grants del usuario de replicación o server_id incorrecto.
Solución: Asegura que las réplicas candidatas estén construidas con configuración “listas para promoción”: binlog activado, server_id único, usuario de replicación presente, log_slave_updates configurado donde se requiera.
3) Síntoma: reparent basado en GTID falla con GTIDs duplicados o faltantes
Causa raíz: Transacciones errantes en una réplica, GTID purgado de forma inconsistente o modos GTID mixtos en la flota.
Solución: Estandariza configuración GTID en todas partes. Durante el incidente, elige un candidato que sea superset de transacciones ejecutadas; reconstruye réplicas inconsistentes en lugar de forzarlas con skips.
4) Síntoma: Seconds_Behind_* es pequeño pero faltan datos tras el failover
Causa raíz: La métrica miente en ciertas condiciones (hilo SQL detenido, aplicación multihilo, drift de reloj) o semi-sync había caído a async.
Solución: Verifica con comparaciones GTID/binlog y señales de reconciliación a nivel de aplicación. Monitoriza contadores de estado de semi-sync, no solo flags de configuración.
5) Síntoma: el lag de replicación explota justo después del failover
Causa raíz: El nodo promovido ya estaba saturado de IO, o el buffer pool está frío, o una transacción larga bloquea el purge y causa contención.
Solución: Elige candidatos de promoción basados en salud (IO, CPU, métricas InnoDB), no solo en “más actualizado”. Calienta réplicas, usa dimensionamiento sensato del buffer pool y evita promover a un host que esté fundiéndose.
6) Síntoma: el script de failover funciona en MySQL pero falla en MariaDB (o viceversa)
Causa raíz: Diferencias de sintaxis (START REPLICA vs START SLAVE), diferencias de variables y semántica GTID distinta.
Solución: Mantén caminos de automatización específicos por motor. Detecta motor/versión antes de ejecutar. Trata “suficientemente compatible” como un riesgo, no como consuelo.
7) Síntoma: tras el failover, las réplicas muestran “connecting” para siempre
Causa raíz: ACL/firewall de red no actualizado, dirección origen equivocada o proxy/VIP movido pero la replicación usa hostnames directos.
Solución: Mantén caminos de tráfico de replicación explícitos y probados. Usa hostnames de replicación dedicados que sean ruteables desde las réplicas. Valida conectividad con checks TCP desde subredes de réplica.
8) Síntoma: el hilo SQL de la réplica se detiene con “DDL mismatch” o errores de metadatos
Causa raíz: Versiones/SQL modes/collations mixtas; replicación basada en sentencias; o un DDL ejecutado de forma distinta.
Solución: Usa replicación basada en filas. Estandariza SQL mode y collation. Evita failover entre versiones cuando sea posible; si es inevitable, prueba el comportamiento de replicación de DDL antes de producción.
Listas de verificación / plan paso a paso
Checklist de preparación para failover (haz esto antes de necesitarlo)
- Fencing existe y está probado: puedes detener que el antiguo primario acepte tráfico incluso si se reinicia.
- Configuración lista para promoción en réplicas: binlog habilitado,
server_idúnico, usuarios de replicación presentes, ajustes GTID correctos. - Postura de durabilidad consistente: decide si aceptas pérdida por crash; no lo conviertas en un ajuste de rendimiento accidental.
- Replicación basada en filas:
binlog_format=ROWsalvo que tengas una razón probada para no hacerlo. - Runbook con ramas por motor: diferencias de sintaxis MySQL vs MariaDB capturadas y ensayadas.
- Señales de salud: aplicación de replicación, latencia de disco, métricas InnoDB, estado semi-sync activo, estado de enrutamiento del proxy.
- Herramienta de probe de escritura: un script mínimo que confirme que el nuevo escritor realmente acepta escrituras por la ruta real del cliente.
Plan de failover en incidente (la versión controlada)
- Detén la hemorragia: pausa automatización, reduce tráfico de escritura si es posible y congela cambios de esquema.
- Fencia el antiguo primario: apagado + aislamiento de red. Verifica que esté abajo e inaccesible desde clientes.
- Elige un candidato: compara posiciones GTID/binlog; rechaza cualquier réplica con errores SQL o deriva; revisa salud IO.
- Detén replicación en el candidato: evita que siga aplicando eventos desde una fuente comprometida.
- Promociona: desactiva read-only; asegúrate que binlog esté activo; verifica que pueda aceptar escrituras.
- Mueve tráfico: actualiza proxy/VIP/DNS; verifica con un probe de escritura; observa tasas de error.
- Reparenta réplicas: usa GTID cuando sea posible; verifica que cada réplica alcance y se mantenga estable.
- Limpieza post-failover: reconstruye réplicas rotas, valida backups y documenta exactamente lo que observaste.
Cuándo elegir MySQL vs MariaDB para entornos con mucho failover de replicación
Elige MySQL (especialmente 8.0+) si:
- Quieres una historia HA de “vendor único” más clara con Group Replication/InnoDB Cluster como opción eventual.
- Tu tooling y memoria operativa del personal ya están orientados a MySQL (Orchestrator, MySQL shell, semántica 8.0).
- Valoras comportamiento predecible de conjuntos GTID y patrones operativos comunes en ofertas gestionadas.
Elige MariaDB si:
- Estás invertido en herramientas del ecosistema MariaDB como MaxScale y entiendes en profundidad su comportamiento de failover.
- Te beneficias de funcionalidades específicas de MariaDB y estás dispuesto a tratar GTID y failover como nativos de MariaDB, no “tipo MySQL”.
- Planeas alrededor de las capacidades de replicación de MariaDB y tu flota es lo suficientemente homogénea para evitar trampas de compatibilidad.
Evita failover entre motores mixtos a menos que hayas probado las versiones exactas, modo GTID, ajustes de binlog y comportamiento de tooling bajo fallo. “Replica en staging” no es una prueba; es un rumor inicial.
Preguntas frecuentes
1) ¿Puedo hacer failover entre réplicas MySQL y MariaDB?
En ocasiones puedes replicar entre ellos en ciertas combinaciones de versiones y configuraciones, pero el failover es riesgoso. Las semánticas GTID difieren y las herramientas suelen asumir un motor. Si debes hacerlo, mantén el rol escritor en una sola familia de motores y trata al otro como consumidor solo-lectura, no como candidato de promoción.
2) ¿GTID garantiza que no hay pérdida de datos durante el failover?
No. GTID te ayuda a identificar qué se ejecutó dónde y facilita reapuntar réplicas. La pérdida de datos depende de qué transacciones fueron reconocidas en el primario antiguo antes de morir y si las réplicas las recibieron/aplicaron.
3) ¿Es semi-sync suficiente para garantizar cero pérdida de datos?
No por sí sola. Semi-sync puede caer a async bajo carga o problemas de red. Debes monitorizar si está activo al momento de la falla y entender los requisitos de ack que configuraste.
4) ¿Por qué a veces “Seconds_Behind_Master” miente?
Porque es una estimación basada en timestamps y estado de hilos. Si el hilo SQL está detenido, si los relay logs están retrasados, si los relojes derivan o si hay aplicación paralela, el número puede ser engañoso. Usa comparaciones GTID/binlog y estado de errores como señales primarias.
5) ¿Cuál es el control de failover más importante?
Fencing. Si no puedes garantizar que el antiguo primario no aceptará escrituras, todo lo demás es teatro.
6) ¿Debo usar replicación basada en sentencias o en filas para failover?
Basada en filas. La replicación por sentencias y formatos mixtos crean deriva no determinista y comportamiento dependiente de la versión. El failover es cuando la deriva se vuelve visible—y costosa.
7) ¿Qué herramientas debo usar para failover?
Usa una herramienta que entienda tu motor y modo GTID, y que soporte fencing o se integre con algo que pueda fencear (o al menos pause promociones hasta que un humano confirme el fencing). Orchestrator es común en flotas MySQL; MaxScale es común en flotas MariaDB; ProxySQL puede funcionar como capa de enrutamiento pero necesita lógica de salud correcta.
8) ¿Cómo elijo la mejor réplica para promover?
Elige la réplica con (a) el conjunto de transacciones ejecutadas/aplicadas más completo, (b) sin errores de replicación y (c) buena salud de host (latencia de disco, recuperación tras fallo no en curso). “La más avanzada pero sobrecargada” es una trampa.
9) ¿Por qué a veces las promociones fallan por “transacciones errantes”?
Porque una réplica ejecutó transacciones que no estaban en el stream de replicación del primario (escrituras manuales, automatización defectuosa o split brain previo). GTID hace esto visible. La solución es reconstruir o reconciliar quirúrgicamente—normalmente reconstruir.
10) ¿Con qué frecuencia debemos ensayar el failover?
Mensualmente si puedes, trimestralmente si debes. Ensayar es cómo descubres que tu proxy cachea DNS por 10 minutos, o que tu “fencing” es en realidad una petición educada.
Conclusión: próximos pasos que puedes hacer esta semana
El failover no se rompe porque MySQL o MariaDB sean “malos”. Se rompe porque confiaste en suposiciones que nunca verificaste bajo fallo. Los motores difieren en semántica GTID, superficie de comandos de replicación y expectativas de tooling del ecosistema. Esas diferencias son manejables—si las reconoces.
Haz esto a continuación, en orden:
- Escribe tu contrato de failover: RPO/RTO aceptables, quién decide perder datos (si es necesario) y qué significa “éxito” (no “la base de datos está arriba”, sino “las escrituras van a exactamente un lugar”).
- Implementa fencing real: no
read_only. Algo que detenga las escrituras del antiguo primario incluso si se reinicia. - Estandariza el modo de replicación: basado en filas, SQL mode/collation consistentes, ajustes GTID consistentes por motor.
- Construye un perfil de réplica listo para promoción: binlog habilitado,
server_idúnico, usuarios/privilegios de replicación presentes y monitorizados. - Haz un ensayo de failover y recoge la evidencia como lo harías en un incidente real: salidas de estado, estadísticas IO, estado del proxy. Arregla la primera sorpresa que encuentres. Habrá sorpresas.
Idea parafraseada de John Allspaw: la confiabilidad viene de aprender continuamente, no de fingir que los sistemas son predecibles bajo presión
.
Broma #2: Lo único más optimista que “el failover es automático” es “el runbook está actualizado”.