MySQL vs MariaDB: explosión de binlogs en disco — cómo mantenerla bajo control

¿Te fue útil?

Todo va bien hasta que deja de ir bien: un servidor de base de datos perfectamente sano de repente empieza a mostrar “No space left on device”, tu aplicación pasa a solo lectura y tu teléfono de guardia adquiere un poderoso campo gravitatorio. Te conectas y descubres que el culpable no es una tabla fuera de control, ni una copia de seguridad rogue, ni siquiera Docker. Es un montón ordenado de binlogs multiplicándose silenciosamente como conejos detrás del cobertizo.

Los binlogs son la memoria de cambios de la base de datos. También son la forma favorita de la base de datos de comerse un disco cuando nadie vigila. Este artículo trata de mantener el crecimiento de los binlogs aburrido —tanto en MySQL como en MariaDB— sin romper la replicación, la recuperación punto en el tiempo (PITR) ni tu fin de semana.

Binlogs: por qué explotan (y por qué los querías)

El binlog binario de MySQL/MariaDB es un registro append-only de cambios. Según la configuración almacena sentencias, imágenes de filas o una mezcla (MIXED). Alimenta la replicación y permite la recuperación punto en el tiempo. También crece por definición: es un diario. Si no lo gestionas explícitamente, seguirá escribiendo hasta que el sistema de archivos te dé una lección de vida.

La mayoría de los incidentes de “explosión de binlog en disco” tienen un conjunto sorprendentemente pequeño de causas raíz:

  • No se configuró retención, por lo que los logs son efectivamente infinitos.
  • La retención existe pero no puede aplicarse, porque un réplica (o herramienta de backup) aún necesita logs antiguos.
  • Amplificación de escritura por el formato ROW, transacciones grandes o actualizaciones masivas que crean binlogs enormes.
  • Tiempo de inactividad de réplicas que obliga al primario a mantener una acumulación cada vez mayor.
  • Suposiciones equivocadas sobre lo que hace “purge”, qué cambia GTID o qué lee tu herramienta de backup.

Una cita operativa que deberías tratar como ley de la física:

“La esperanza no es una estrategia.” — Gene Kranz

La gestión de binlogs no es “configúralo y olvídalo.” Es “configúralo y monitorízalo”, con una política de retención que coincida con los objetivos de recuperación del negocio y la realidad de tu topología de replicación.

Datos interesantes y un poco de historia (porque el contexto previene errores)

  • Dato 1: El binlog de MySQL precede al marketing moderno de “CDC”. Fue un mecanismo pragmático de replicación mucho antes de que los “event streams” fueran populares.
  • Dato 2: La replicación basada en sentencias fue el valor predeterminado original; era más pequeña pero vulnerable a no determinismos (piensa en NOW(), RAND() y consultas que “dependen de los datos”).
  • Dato 3: El registro basado en filas (ROW) se convirtió en el predeterminado práctico en muchos entornos porque es determinista, pero puede inflarse mucho en actualizaciones masivas.
  • Dato 4: GTID (Global Transaction ID) se introdujo para hacer más saneado el failover de replicación; no eliminó la necesidad de disciplina en la retención de binlogs.
  • Dato 5: MariaDB se separó de MySQL y añadió su propia implementación de GTID; las semánticas operativas no son idénticas, especialmente en nombres y variables de estado.
  • Dato 6: Las opciones de “expire logs” han cambiado de nombre a lo largo de los años en MySQL, y los posts antiguos suelen confundir a quien corre versiones nuevas.
  • Dato 7: Muchos sistemas de backup que aseguran “no-lock” siguen dependiendo de binlogs para consistencia entre tablas; borrar logs sin coordinarse con ellos es un clásico auto-sabotaje.
  • Dato 8: Los relay logs en réplicas también pueden explotar; la gente culpa a los binlogs del primario mientras la réplica acapara su propio backlogs en silencio.

Broma #1: Los binlogs son como recibos. No los necesitas para siempre, pero te arrepentirás de triturarlos cinco minutos antes de una auditoría.

MySQL vs MariaDB: las diferencias que importan para el crecimiento de binlogs

A primera vista, la gestión de binlogs parece igual: establecer retención, asegurar que la purga es segura, vigilar la replicación. De cerca, MySQL y MariaDB tienen suficientes diferencias como para arruinarte el día si copias y pegas consejos entre ellos.

1) Perillas de retención: intención similar, nombres y casos límite distintos

MySQL históricamente usó expire_logs_days. Las versiones más nuevas prefieren binlog_expire_logs_seconds. Muchos sistemas todavía tienen ambos en la configuración porque a nadie le gusta borrar líneas antiguas.

MariaDB soporta expire_logs_days también, y en versiones más recientes tiene binlog_expire_logs_seconds. Pero el comportamiento efectivo puede variar según la versión y cómo ejecutas replicación y backups. No adivines; verifica con SHOW VARIABLES y observando purgas reales.

2) Comportamiento de GTID: el concepto se solapa; los detalles operativos no

GTID de MySQL está integrado estrechamente con el estado de replicación (gtid_executed, gtid_purged) e intenta hacer el tooling de failover consistente. GTID de MariaDB es similar en espíritu, pero difiere en representación y en algunos flujos de trabajo de failover.

Para la retención de binlogs, el punto clave es claro: GTIDs no reducen el uso de disco por sí mismos. Reducen la carga cognitiva de “qué aplicamos dónde”, lo que ayuda a eliminar logs de forma segura, pero aún necesitas una política de retención y procedimientos de purga seguros.

3) Configuraciones predeterminadas y “seguro por defecto” varían por distribución

Las configuraciones empaquetadas en entornos empresariales a veces vienen con binlogging activado y sin expiración configurada porque “podría necesitarse replicación más tarde”. Eso no es precaución; es fallo diferido.

4) Compresión y cifrado de binlogs: buenas funciones, pero no gratuitas

Según las versiones, puede haber opciones para cifrar y comprimir binlogs. El cifrado incrementa la carga CPU; la compresión intercambia CPU por reducción de disco e I/O. Ambas valen la pena, pero solo después de tener los fundamentos (retención + salud de replicación) bajo control.

Guía rápida de diagnóstico: encuentra el cuello de botella en minutos

Esta es la “tienes 10 minutos antes de que el disco se llene” guía. No discutas arquitectura mientras el servidor hace swap.

Primero: confirma que son binlogs y cuantifica la tasa de crecimiento

  • ¿Cuánto espacio libre queda en disco?
  • ¿Qué tamaño tiene el directorio de binlogs?
  • ¿Se crean archivos binlog nuevos rápidamente, o es un solo archivo el que crece velozmente?

Segundo: comprueba si la purga está bloqueada por replicación o herramientas

  • ¿Alguna réplica está con mucho retardo o offline?
  • ¿Algún proceso de backup está leyendo binlogs antiguos?
  • ¿El servidor está configurado con retención y realmente está purgando?

Tercero: decide la acción de emergencia menos mala

  • Si la replicación está sana, purga de forma segura según la posición/GTID de réplicas.
  • Si la replicación está enferma, o arreglas la réplica rápido, o aceptas que la romperás y planificas reconstruirla.
  • Si te quedas sin disco ahora, gana tiempo: amplía el sistema de archivos, mueve binlogs o detén temporalmente escrituras (throttle de la app) mientras recuperas el control.

La prioridad táctica siempre es la misma: evitar un fallo que corrompa tablas y convierta un incidente manejable en un evento de restauración.

Tareas prácticas (con comandos): medir, decidir, arreglar

No puedes gestionar lo que no mides, y no puedes purgar de forma segura lo que no entiendes. Aquí hay tareas concretas que puedes ejecutar en servidores reales. Cada tarea incluye: comando, salida de ejemplo, lo que significa y la decisión que tomas.

Tarea 1: Comprueba el margen de disco y qué sistema de archivos está en riesgo

cr0x@server:~$ df -hT
Filesystem     Type   Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4   450G  430G   20G  96% /
/dev/nvme1n1p1 xfs    1.8T  1.1T  700G  62% /var/lib/mysql

Significado: Estás cerca del límite en / pero MySQL vive en otro montaje con margen. Si los binlogs están en /, te quedan minutos.

Decisión: Confirma datadir de MySQL y dónde se escriben los binlogs; muévelos fuera del filesystem raíz si hace falta.

Tarea 2: Encuentra dónde viven realmente los binlogs (no asumas)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'log_bin%'; SHOW VARIABLES LIKE 'datadir';"
+---------------+---------------------------+
| Variable_name | Value                     |
+---------------+---------------------------+
| log_bin       | ON                        |
| log_bin_basename | /var/lib/mysql/binlog   |
| log_bin_index | /var/lib/mysql/binlog.index |
+---------------+---------------------------+
+---------------+--------------------+
| Variable_name | Value              |
+---------------+--------------------+
| datadir       | /var/lib/mysql/    |
+---------------+--------------------+

Significado: Los binlogs están bajo /var/lib/mysql. Bien. Si hubieras visto algo como /var/log o un montaje root pequeño, ya tendrías la pistola humeante.

Decisión: Si la ruta de binlogs está en un filesystem limitado, planifica una reubicación usando la configuración y un reinicio (o reconfigura si está soportado); no hagas symlinks a medianoche salvo que sea vida o muerte.

Tarea 3: Identifica el tamaño del directorio de binlogs y los archivos más grandes

cr0x@server:~$ sudo du -sh /var/lib/mysql/binlog* 2>/dev/null | sort -h | tail -n 5
1.0G    /var/lib/mysql/binlog.000812
1.0G    /var/lib/mysql/binlog.000813
1.0G    /var/lib/mysql/binlog.000814
1.0G    /var/lib/mysql/binlog.000815
812G    /var/lib/mysql/binlog.index

Significado: La línea de binlog.index en esta salida es engañosa porque du cuenta bloques referenciados de forma rara para patrones sparse y metadata; la historia real es que existen muchos archivos binlog y cada uno es ~1G.

Decisión: Cuenta archivos y calcula el tamaño total con una herramienta más precisa (siguiente tarea). Si generas muchos archivos de 1G rápidamente, la rotación ocurre; el crecimiento sigue siendo real.

Tarea 4: Cuenta archivos binlog y bytes totales con precisión

cr0x@server:~$ sudo find /var/lib/mysql -maxdepth 1 -type f -name 'binlog.*' -printf '%s\n' | awk '{sum+=$1} END{printf "files=%d total=%.2fG\n", NR, sum/1024/1024/1024}'
files=815 total=812.45G

Significado: Mantienes ~812G de binlogs. Eso no es “un pequeño backlog”, es una decisión organizacional que nadie tomó a propósito.

Decisión: Determina inmediatamente si réplicas o flujos de backup requieren ese historial. Si no, purga de forma segura. Si sí, arregla el bloqueo o acepta reconstruir réplicas.

Tarea 5: Confirma variables de retención (lo que el servidor piensa)

cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('expire_logs_days','binlog_expire_logs_seconds','sync_binlog','binlog_format','binlog_row_image');"
+---------------------------+-----------+
| Variable_name             | Value     |
+---------------------------+-----------+
| binlog_expire_logs_seconds| 0         |
| binlog_format             | ROW       |
| binlog_row_image          | FULL      |
| expire_logs_days          | 0         |
| sync_binlog               | 1         |
+---------------------------+----------------+

Significado: La retención está efectivamente deshabilitada. Además, ROW + FULL puede ser enorme para tablas anchas y cargas con muchas actualizaciones. sync_binlog=1 es durable pero puede amplificar I/O.

Decisión: Establece un valor de retención que coincida con tus objetivos de recuperación y la realidad de replicación. Considera binlog_row_image=MINIMAL donde sea seguro y esté soportado, pero valida primero requisitos de aplicación y replicación.

Tarea 6: Ver qué binlogs existen desde la perspectiva de MySQL

cr0x@server:~$ mysql -e "SHOW BINARY LOGS;" | tail -n 6
| binlog.000810 | 1073741961 |
| binlog.000811 | 1073741982 |
| binlog.000812 | 1073741991 |
| binlog.000813 | 1073742005 |
| binlog.000814 | 1073742011 |
| binlog.000815 |  932145331 |

Significado: El servidor está rastreando binlogs y sus tamaños. Esta salida es contra lo que purgas: no borres archivos a mano y esperes que MySQL aplauda.

Decisión: Si necesitas purgar, hazlo vía SQL (PURGE BINARY LOGS) o mediante flujos que reconozcan mysqlbinlog. El borrado manual es el último recurso y suele ir seguido de “¿por qué no arranca?”

Tarea 7: Comprueba el estado de replicación en una réplica (archivo/posición clásico)

cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep 'Source_Log_File|Read_Source_Log_Pos|Relay_Source_Log_File|Exec_Source_Log_Pos|Seconds_Behind_Source|Replica_IO_Running|Replica_SQL_Running' -n
12:Source_Log_File: binlog.000702
13:Read_Source_Log_Pos: 98433122
28:Relay_Source_Log_File: binlog.000702
29:Exec_Source_Log_Pos: 98433122
34:Seconds_Behind_Source: 0
40:Replica_IO_Running: Yes
41:Replica_SQL_Running: Yes

Significado: Esta réplica está al día y ejecutando logs actuales. Si todas las réplicas se ven así, normalmente puedes purgar binlogs antiguos de forma segura hasta el archivo Source_Log_File más antiguo que aún se necesite.

Decisión: Recoge esto en cada réplica. El “binlog más antiguo necesario” entre réplicas es tu frontera de purga.

Tarea 8: Comprueba el estado de replicación en una réplica (sabor GTID)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'gtid_mode'; SHOW MASTER STATUS\G; SHOW REPLICA STATUS\G" | egrep 'gtid_mode|Executed_Gtid_Set|Retrieved_Gtid_Set|File:|Position:|Seconds_Behind_Source' -n
1:gtid_mode	ON
7:File: binlog.000815
8:Position: 932145331
19:Retrieved_Gtid_Set: 2f1d3c0a-2f9d-11ee-8f6a-0242ac120002:1-93381221
20:Executed_Gtid_Set: 2f1d3c0a-2f9d-11ee-8f6a-0242ac120002:1-93381221
26:Seconds_Behind_Source: 0

Significado: GTID está habilitado y la réplica está sincronizada. La purga sigue basándose en lo que las réplicas necesitan; GTID solo te da un estado más limpio para razonar.

Decisión: Usa conjuntos GTID para validar que las réplicas han ejecutado lo que planeas purgar. Si una réplica falta partes del conjunto, no puedes purgar esos logs sin aceptar una reconstrucción.

Tarea 9: Identifica una réplica que bloquea purgas (offline o retrasada)

cr0x@server:~$ mysql -e "SHOW REPLICA HOSTS;"
+-----------+------------------+------+-----------+--------------------------------------+
| Server_id | Host             | Port | Rpl_recovery_rank | Master_id                    |
+-----------+------------------+------+-----------+--------------------------------------+
| 102       | db-replica-a     | 3306 | 0         | 101                                  |
| 103       | db-replica-b     | 3306 | 0         | 101                                  |
| 104       | db-replica-c     | 3306 | 0         | 101                                  |
+-----------+------------------+------+-----------+--------------------------------------+

Significado: Tienes tres réplicas registradas. “Registrada” no significa “saludable”.

Decisión: Conéctate a cada réplica y ejecuta Tarea 7/8. La peor dictará tu piso de retención a menos que estés dispuesto a prescindir de ella y reconstruirla.

Tarea 10: Comprueba el crecimiento de relay logs en una réplica (la otra bomba de disco)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'relay_log%';"
+-------------------+-----------------------------------+
| Variable_name     | Value                             |
+-------------------+-----------------------------------+
| relay_log         | /var/lib/mysql/relaylog           |
| relay_log_index   | /var/lib/mysql/relaylog.index     |
| relay_log_info_file | relay-log.info                  |
+-------------------+-----------------------------------+
cr0x@server:~$ sudo find /var/lib/mysql -maxdepth 1 -type f -name 'relaylog.*' -printf '%s\n' | awk '{sum+=$1} END{printf "relay files=%d total=%.2fG\n", NR, sum/1024/1024/1024}'
relay files=390 total=412.77G

Significado: La réplica está acumulando relay logs. Esto ocurre cuando el hilo SQL no puede mantenerse al día, o está detenido o falla repetidamente. La gente suele “arreglar” la retención de binlogs del primario mientras la réplica es la que llena discos.

Decisión: Arregla la tasa de aplicación de la réplica (índices, errores del hilo SQL, replicación paralela) o reconstruyela. También confirma que la purga de relay log está habilitada y funcionando.

Tarea 11: Busca transacciones largas que creen eventos binlog gigantes

cr0x@server:~$ mysql -e "SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS age_s, trx_rows_modified FROM information_schema.innodb_trx ORDER BY age_s DESC LIMIT 5;"
+--------+---------------------+-------+-------------------+
| trx_id | trx_started         | age_s | trx_rows_modified |
+--------+---------------------+-------+-------------------+
| 987655 | 2025-12-31 09:11:02 | 18420 | 2289341           |
| 987654 | 2025-12-31 13:55:10 |   122 | 0                 |
+--------+---------------------+-------+-------------------+

Significado: Una transacción de 5 horas que modifica millones de filas es una fábrica de binlogs. Aunque sea “legítima”, es un riesgo operativo: estresa disco, replicación y recuperación tras fallo.

Decisión: Coordina con los propietarios de la aplicación: segmenta actualizaciones grandes, evita transacciones monstruo y considera mecanismos de limitación.

Tarea 12: Mide el throughput de escritura actual de binlogs (¿es un pico repentino?)

cr0x@server:~$ sudo iostat -dx 1 3 | egrep 'Device|nvme1n1'
Device            r/s     w/s   rMB/s   wMB/s avgrq-sz avgqu-sz await  svctm  %util
nvme1n1          12.0   980.0    0.4    86.2    176.2     4.12   4.1   0.7   72.0
nvme1n1          10.0  1100.0    0.3    92.5    172.0     6.88   6.2   0.8   89.0
nvme1n1          11.0  1205.0    0.3   101.1    171.4     8.40   7.1   0.9   96.0

Significado: Escrituras sostenidas intensas. Si esto se correlaciona con el crecimiento de binlogs, puedes estar en una tormenta de escrituras (job por lotes, migración de esquema, bucle de reintento).

Decisión: Identifica los mayores escritores (siguiente tarea) y decide si pausarlos, limitarlos o optimizarlos.

Tarea 13: Identifica las consultas que más escriben (digest de performance_schema)

cr0x@server:~$ mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, SUM_ROWS_AFFECTED, ROUND(SUM_TIMER_WAIT/1e12,1) AS total_s FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_ROWS_AFFECTED DESC LIMIT 3\G"
*************************** 1. row ***************************
DIGEST_TEXT: UPDATE orders SET status = ? WHERE created_at < ? AND status = ?
COUNT_STAR: 412
SUM_ROWS_AFFECTED: 18922341
total_s: 980.2
*************************** 2. row ***************************
DIGEST_TEXT: DELETE FROM sessions WHERE expires_at < ?
COUNT_STAR: 8122
SUM_ROWS_AFFECTED: 4491122
total_s: 122.8

Significado: La carga está haciendo grandes updates y deletes. En formato ROW, eso es carga de binlog, no solo “trabajo de base de datos”.

Decisión: Añade índices para reducir filas tocadas, segmenta operaciones o programa mantenimiento pesado fuera de horas pico. Si es una migración puntual, planifica margen de disco extra y una frontera de purga.

Tarea 14: Comprueba si la purga automática está funcionando (estado de expiración de binlogs)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Binlog%';"
+---------------------------+----------+
| Variable_name             | Value    |
+---------------------------+----------+
| Binlog_cache_disk_use     | 22       |
| Binlog_cache_use          | 98121    |
| Binlog_stmt_cache_disk_use| 0        |
| Binlog_stmt_cache_use     | 0        |
+---------------------------+----------+

Significado: No es directamente “estado de purga”, pero te indica que el binlogging está activo y el comportamiento de caché. Para validar purgas observa si los archivos binlog antiguos desaparecen con el tiempo y revisa las variables de retención.

Decisión: Si la retención está establecida y los archivos no desaparecen, probablemente la replicación/backups están bloqueando la purga o existe una discrepancia de comportamiento por versión.

Tarea 15: Purga binlogs de forma segura hasta una frontera (basado en archivo)

cr0x@server:~$ mysql -e "PURGE BINARY LOGS TO 'binlog.000780';"

Significado: MySQL/MariaDB eliminará archivos binlog estrictamente anteriores a binlog.000780 (sin incluirlo), actualizando el índice y el estado interno.

Decisión: Haz esto solo después de confirmar que todas las réplicas y consumidores (backup/CDC) han avanzado más allá de esa frontera. Si una réplica aún necesita binlog.000702, purgar hasta 780 la romperá.

Tarea 16: Emergencia: rota a un nuevo archivo binlog (gana claridad)

cr0x@server:~$ mysql -e "FLUSH BINARY LOGS;"

Significado: Fuerza una rotación de binlog. Esto no reduce el uso de disco, pero puede ayudar operacionalmente al hacer obvio “qué es actual” y crear una frontera limpia para decisiones de purga posteriores.

Decisión: Úsalo durante la respuesta a incidentes cuando quieras un punto de inicio conocido para análisis y aislar la actividad actual del backlog.

Retención y purga: valores seguros y aristas peligrosas

Los binlogs no son “basura”. Son “seguridad temporal”. Tu trabajo es decidir cuánta seguridad puedes permitirte y luego aplicarla sin piedad.

Elige una ventana de retención basada en objetivos de recuperación, no en sensaciones

Si haces backups completos diarios y quieres recuperación punto en el tiempo dentro de ese día, normalmente necesitas los binlogs desde el momento del backup. Muchas organizaciones eligen 2–7 días para cubrir holguras operativas: detección tardía, despliegues malos, turnos de fin de semana, tiempo para reconstruir réplicas.

Pero: la retención está limitada por el consumidor más lento. Si tienes una réplica frecuentemente offline, un conector CDC que pausa o un sistema de backup que “streaming de binlogs cuando le place”, tu primario mantendrá más de lo que tu política dicta a menos que elimines esa dependencia.

Configura retención explícitamente (y confirma qué variable usa tu versión)

En MySQL moderno, binlog_expire_logs_seconds es la perilla más clara. En instalaciones antiguas, expire_logs_days puede ser la única que importe. MariaDB varía por versión y algunos entornos mantienen ambas. Quieres una configuración autoritativa y pruebas de que funciona.

Consejo operativo: si heredas un servidor sin retención, no pongas “30 días” como primer movimiento “seguro”. Así es como mantienes la bomba bajo tu escritorio. Empieza con una ventana realista—a menudo 3–7 días—y aumenta solo si has demostrado que puedes almacenarlo y realmente lo necesitas.

No purgues borrando archivos

Sí, puedes rm /var/lib/mysql/binlog.000123. Y sí, a veces sale bien. Pero así es como consigues:

  • réplicas quedándose pidiendo binlogs que ya no existen
  • el servidor confundido sobre su índice de binlog
  • workflows de backup/PITR rotos de formas sutiles

Usa PURGE BINARY LOGS a menos que el servidor esté caído y estés haciendo cirugía de desastre. Aun entonces, planifica un reinicio y espera consecuencias en la replicación.

Retardo en replicación: la razón más común por la que la purga “no funciona”

Cuando la gente dice “puse retención pero los binlogs siguen creciendo”, nueve de cada diez veces están ejecutando replicación y algo va atrasado. El primario mantiene logs porque las réplicas los necesitan. Eso no es que el servidor te desobedezca; es que pides cosas mutuamente excluyentes: “guarda menos historial” y “soporta una réplica con semanas de retraso”.

Cómo la replicación bloquea la purga en la práctica

Bloqueadores comunes:

  • Una réplica está offline por mantenimiento, problemas de red o “alguien la paró” y se olvidó.
  • El hilo SQL de la réplica está detenido debido a un error (clave duplicada, tabla faltante tras un cambio de esquema mal hecho, etc.).
  • La réplica está limitada por I/O aplicando eventos ROW y no puede ponerse al día.
  • La réplica tiene relay logs enormes y está atascada en la aplicación lenta.
  • Consumidores no réplica (herramientas CDC, pipelines de auditoría, agentes de backup) están leyendo binlogs y requieren historial.

Sé decisivo con réplicas no saludables

Si una réplica ha estado offline tanto que su backlog está comiendo el disco del primario, tienes que decidir:

  • ¿La réplica es crítica? Si sí, arréglala ahora: restaura red, resuelve errores SQL, aumenta paralelismo de aplicación si procede o reconstruyela desde una snapshot fresca.
  • ¿Es “agradable tener”? Si no, rómpela a propósito: purga binlogs para salvar el primario y luego reconstruye la réplica más tarde.

Lo que no debes hacer es mantener el primario en estado casi lleno para preservar una réplica descuidada. Así conviertes una cosa rota en dos.

PITR y copias de seguridad: cómo mantener la recuperación sin logs infinitos

Si quieres recuperación punto en el tiempo, necesitas dos ingredientes:

  1. una copia base consistente (completa o cadena incremental)
  2. binlogs desde el momento de esa copia base hasta el punto de recuperación

El modo de fallo es obvio: guardas binlogs “por si acaso”, pero en realidad no tienes una cadena válida de copias base. Entonces estás gastando disco por la ilusión de seguridad. Valida tu proceso de restauración. Practícalo. Si no, los binlogs son solo confort muy caro.

Lógica práctica de retención que funciona

  • Mantén binlogs al menos por el tiempo máximo que puedas necesitar para detectar un problema de datos (la latencia humana de detección es real).
  • Mantén binlogs al menos por un intervalo completo de backup más un margen. Si los backups son diarios, 2–7 días es común.
  • Alinea fronteras de purga con la completación de backups. Purgar logs que un backup no ha “capturado” es cómo pierdes PITR.

Broma #2: Lo único más aterrador que un disco lleno de binlogs es un disco sin binlogs cinco minutos después de que los necesitas.

Elección de formato de binlog: ROW vs MIXED vs STATEMENT

El tamaño del binlog no es solo “tráfico”. También es “cómo representas el tráfico”. Las opciones de formato pueden cambiar drásticamente el volumen de binlogs.

ROW: determinista, pesado y generalmente la elección correcta

El formato ROW registra los cambios reales de filas. Eso es oro para la corrección y seguridad de replicación. También es costoso cuando actualizas muchas filas, o cuando las filas son anchas (muchas columnas), o cuando tus updates tocan blobs/text.

Si estás en ROW y ves una explosión de binlogs, no te lances inmediatamente a STATEMENT. Primero, reduce el radio de destrucción de la carga: segmenta updates, indexa correctamente y evita tocar columnas innecesarias.

binlog_row_image: FULL vs MINIMAL

Donde esté soportado y sea seguro, binlog_row_image=MINIMAL puede reducir mucho el tamaño del binlog al registrar menos columnas. Esto ayuda bastante en tablas anchas donde las actualizaciones solo afectan unas pocas columnas.

Pero: “seguro” depende de versión, herramientas y si los consumidores downstream requieren imágenes completas. Algunas configuraciones CDC quieren FULL. Algunos pipelines de auditoría dependen de ello. Conoce a tus consumidores.

MIXED y STATEMENT: más pequeños, pero con aristas

El logging basado en sentencias puede ser mucho más pequeño para operaciones masivas (una sola sentencia en lugar de millones de eventos por fila). También puede ser no determinista y romper replicación de maneras raras. MIXED intenta ser listo: registra sentencias cuando es seguro, filas cuando no.

En entornos de producción modernos, ROW es la elección aburrida que te mantiene fuera de los postmortems. Si optimizas tamaño de binlog cambiando de formato, hazlo solo después de probar la corrección de replicación con tu carga exacta.

Realidad del almacenamiento: IOPS, fsync, compresión y dónde van los bytes

El crecimiento de binlogs es un problema tanto de base de datos como de almacenamiento. Si tus discos son lentos, los binlogs pueden ser síntoma y causa: escrituras pesadas llenan el disco, la presión de I/O ralentiza la aplicación en réplica, lo que obliga a mayor retención, y se llena más el disco.

sync_binlog y compensaciones de durabilidad

sync_binlog=1 significa que el servidor hace fsync frecuentemente en escrituras de binlog. Es bueno para durabilidad (menos transacciones perdidas en un crash) y a menudo requerido para garantías estrictas de recuperación. También castiga más la latencia del almacenamiento.

Si tu almacenamiento no lo soporta, no “optimices” relajando la durabilidad a ciegas. Arregla el almacenamiento, reparte el I/O o escala. Los parámetros de durabilidad no son perillas de rendimiento; son perillas de riesgo.

Separa binlogs en un filesystem dedicado (a veces)

Poner binlogs en su propio montaje puede ser una buena idea:

  • evita que los binlogs llenen el filesystem del datadir y dejen el servidor inutilizable
  • simplifica la monitorización y las alertas
  • puede alinearse con medios más rápidos

Pero también puede crear contención si los colocas en discos lentos y compartidos. La meta es aislamiento con rendimiento adecuado, no “muévelo a otro sitio y reza”.

Compresión: útil cuando la red/I/O es el cuello de botella

Si tu payload de binlog es enorme y el cuello de botella es disco o red, la compresión puede ayudar. Si tu CPU ya es cuello de botella, puede empeorar. Mide primero.

Tres microhistorias corporativas desde el terreno

Microhistoria 1: El incidente causado por una suposición errónea

Tenían un primario y dos réplicas. Sencillo. El equipo también tenía un requisito de cumplimiento para mantener “suficiente historial para investigar”. Alguien interpretó eso como “mantener binlogs indefinidamente”, porque los binlogs parecen un rastro de auditoría si miras con mucha imaginación.

La suposición errónea apareció meses después durante un parche de kernel rutinario. Una réplica estuvo abajo más tiempo de lo esperado por un problema de firmware NIC, por lo que se perdió muchos cambios. El primario guardó binlogs diligentemente para que la réplica pudiera ponerse al día. Nadie lo notó porque el primario aún tenía bastante disco. Al principio.

Entonces llegó un job por lotes: un backfill grande actualizando una tabla ancha en formato ROW. Los binlogs se dispararon. El primario llenó su filesystem en plena jornada y dejó de aceptar escrituras. El outage no empezó con el job por lotes; empezó con la suposición de que “los binlogs son un log de auditoría” y que “la retención puede ser infinita”.

Después implementaron dos cosas: una canalización real de auditoría (separada de binlogs) y una retención explícita alineada con backup/PITR. La réplica que quedó offline se reconstruyó desde una snapshot fresca. A nadie le gustó, pero todos durmieron tranquilos.

Microhistoria 2: La optimización que salió mal

Otro equipo luchaba con retardo de replicación y uso de disco. Alguien tuvo una idea brillante: reducir el tamaño de binlog cambiando de ROW a STATEMENT. Funcionó en laboratorio. Incluso funcionó una semana en producción. Así suelen empezar estas historias.

Luego un despliegue introdujo un patrón de consulta que dependía de comportamiento no determinista: una actualización basada en la hora actual implícita. En el primario resultó de una forma; en una réplica con timing ligeramente distinto resultó otra. Las réplicas divergingaron en silencio—sin crash, sin alarma fuerte, solo datos incorrectos.

El fallo fue caro porque fue sutil. Pasaron días reconciliando tablas, re-clonando réplicas y construyendo chequeos de detección. La “optimización” de binlog ahorró disco y les costó confianza.

Volvieron a ROW. La solución real fue aburrida: jobs segmentados, mejores índices y una política de retención que permitiera reconstruir réplicas rápidamente cuando fuera necesario. El disco es más barato que la corrección, hasta que no lo es.

Microhistoria 3: La práctica aburrida pero correcta que salvó el día

Esta es mi favorita porque no es heroica. Es competente. Una compañía ejecutaba primarios MySQL con tres réplicas en dos regiones. Trataron la retención de binlogs como parte de la planificación de capacidad, no como una ocurrencia tardía.

Tenían un runbook: verificar semanalmente que la posición de la réplica más antigua estuviera dentro de la retención; mensual realizar un drill de PITR en un sandbox; alerta diaria si el crecimiento del filesystem de binlogs excedía un umbral por hora. Las alertas eran silenciosas la mayor parte del tiempo, que es el objetivo.

Un día una partición de red aisló una réplica por horas. Los binlogs crecieron, pero las alertas saltaron temprano, y el de guardia pudo ver la tasa de crecimiento y la ventana de retención. Ampliaron el filesystem de binlogs (porque tenían un procedimiento aprobado), trajeron la réplica de vuelta y purgaron logs antiguos de forma segura después de que se pusiera al día.

No hubo outage. No hubo war room. Solo un ticket, un gráfico y un par de comandos. El mejor incidente es el que nunca se convierte en historia para contar en una conferencia.

Errores comunes: síntoma → causa raíz → solución

1) Disco se llena aunque hayas configurado retención

Síntoma: Los archivos binlog siguen acumulándose; el archivo más antiguo tiene semanas; las variables de retención están establecidas.

Causa raíz: Una réplica o consumidor de binlogs está atrasado/offline, impidiendo la purga (o configuraste la variable equivocada para tu versión y se ignora).

Solución: Identifica el binlog más antiguo requerido entre réplicas, arregla o reconstruye la réplica atrasada, verifica la variable de retención correcta con SHOW VARIABLES y valida que los archivos vayan desapareciendo.

2) La purga rompe la replicación inmediatamente

Síntoma: Errores del hilo I/O de réplica: “Could not find first log file name in binary log index file.”

Causa raíz: Purgaste más allá de lo que la réplica había recuperado/ejecutado, o borraste archivos manualmente.

Solución: Reconstruye la réplica desde una snapshot fresca, o si es posible redirígela a una fuente que aún tenga los logs requeridos. Deja de borrar binlogs a mano.

3) Disco de la réplica se llena pero el primario parece bien

Síntoma: La réplica se queda sin disco; el primario tiene espacio de sobra.

Causa raíz: Los relay logs se acumulan porque el hilo SQL está detenido/lento; o el comportamiento de relay_log_purge no es efectivo debido a errores.

Solución: Arregla el error del hilo SQL, mejora el throughput de aplicación, confirma que la purga de relay log funciona, o reconstruye la réplica.

4) Los binlogs son enormes durante backfills/migraciones

Síntoma: Un solo job crea cientos de GB de binlogs; las réplicas se atrasan horas.

Causa raíz: Transacciones grandes, formato ROW, filas anchas y updates/deletes sin segmentar.

Solución: Segmenta operaciones, añade/verifica índices, evita tocar columnas innecesarias, considera binlog_row_image=MINIMAL donde sea seguro y programa trabajos pesados con margen de capacidad.

5) “Necesitamos binlogs para auditoría” se convierte en “perdimos un fin de semana”

Síntoma: Retención sin fin justificada por cumplimiento; crecimiento constante del disco; nadie puede exponer un procedimiento de recuperación o investigación.

Causa raíz: Los binlogs se usan como sustituto de una estrategia de auditoría.

Solución: Construye una canalización de auditoría/CDC diseñada para retención y consulta; mantén binlogs solo mientras sean necesarios para replicación/PITR.

Listas de verificación / plan paso a paso

Paso a paso: controla el crecimiento de binlogs (sin romper cosas)

  1. Inventario de consumidores: lista réplicas, herramientas CDC y agentes de backup que leen binlogs.
  2. Mide la huella actual de binlogs: tamaño total, cantidad de archivos, tasa de crecimiento por hora/día.
  3. Verifica ajustes de retención: confirma qué variable aplica a tu versión; elimina configuraciones heredadas conflictivas.
  4. Verifica que la purga sea posible: recopila posiciones de réplicas/conjuntos GTID; encuentra la frontera más antigua requerida.
  5. Arregla réplicas atrasadas/offline: o las pones al día o decides reconstruir; no las dejes secuestrar al primario.
  6. Purga de forma segura: usa comandos SQL de purga hasta una frontera validada.
  7. Configura alertas: uso de disco, tasa de crecimiento del directorio de binlogs, retardo de réplicas, crecimiento de relay logs en réplicas.
  8. Reduce la amplificación de escritura: segmenta jobs, indexa, reconsidera ajustes de row image donde sea seguro.
  9. Alinea con backups: asegura una baseline consistente de backup y pasos documentados de PITR.
  10. Practica la recuperación: drills periódicos de PITR. Si no puedes restaurar, la retención es teatro.

Checklist de emergencia: el disco está al 95% y subiendo

  1. Confirma qué filesystem está lleno (df -hT).
  2. Confirma que los binlogs son el uso dominante (du / find tamaño total).
  3. Identifica la posición/GTID más antigua de réplica y si alguna réplica está offline.
  4. Si las réplicas están sanas: purga hasta la frontera segura.
  5. Si una réplica está offline y no es crítica: acepta romperla, purga para salvar al primario y reconstruyela después.
  6. Si no puedes purgar lo suficientemente rápido: amplía el filesystem o mueve binlogs para ganar tiempo, luego aplica la solución correcta.

Preguntas frecuentes

1) ¿Puedo simplemente desactivar el binlogging para detener el crecimiento de disco?

Puedes, pero probablemente no deberías. Desactivar binlogs rompe la replicación y elimina PITR. Si realmente no necesitas ninguno de los dos, apágalo deliberadamente y actualiza las suposiciones operativas. De lo contrario, arregla retención y salud de replicación.

2) ¿Por qué no borró nada expire_logs_days?

O bien está puesto en 0 (deshabilitado), estás en una versión donde otra variable tiene prioridad, o algo aún necesita los logs (réplica/consumidor). Verifica con SHOW VARIABLES y luego revisa el estado de las réplicas.

3) ¿Cuál es la forma más segura de purgar binlogs?

Usa SQL: PURGE BINARY LOGS TO 'binlog.XXXXXX'; después de confirmar que todas las réplicas/consumidores ya no necesitan logs anteriores. El proceso humano más seguro es calcular la frontera de purga desde la réplica más antigua y documentarlo en el ticket del incidente.

4) ¿Son iguales los comandos de purga en MariaDB y MySQL?

Los comandos SQL básicos son muy similares. Las diferencias que molestan suelen estar en la implementación de GTID, variables específicas de versión y lo que espera tu tooling. Trata cada flota como su propio ecosistema: verifica el comportamiento, no confíes en la memoria.

5) ¿Cuánta retención de binlogs debo mantener?

Mantén lo suficiente para cubrir: (a) el tiempo desde tu última copia base válida hasta ahora, más (b) latencia de detección/respuesta, más (c) tiempo de reconstrucción de réplicas si dependes de ellas. Para muchos equipos eso es 3–7 días. Para otros 24 horas. Si no puedes justificarlo con una historia de recuperación, no lo necesitas.

6) ¿Los GTIDs reducen el tamaño de binlog?

No. Los GTID te ayudan a razonar sobre el estado de replicación y failover. El tamaño del binlog lo domina la carga y el formato (ROW vs STATEMENT), las imágenes de fila y los patrones de transacción.

7) ¿Por qué los binlogs son enormes cuando ejecutamos un DELETE?

En formato ROW, un DELETE grande registra eventos por cada fila afectada. La base de datos está haciendo trabajo real y registrándolo. La solución es segmentar deletes, asegurar índices adecuados y programar mantenimiento pesado con margen de capacidad.

8) Si una réplica está semanas atrás, ¿debería mantener binlogs hasta que se ponga al día?

Normalmente no. Si una réplica está tan atrás, a menudo es más rápido y seguro reconstruirla desde una snapshot fresca. Mantener semanas de binlogs en el primario es cómo pones en riesgo la disponibilidad del primario por una réplica que ya te está fallando.

9) ¿Y la “explosión de binlog en disco” en una réplica?

Eso suele ser relay logs, no binlogs. Revisa el tamaño del directorio de relay log de la réplica y si la aplicación SQL está detenida. Arregla la aplicación o reconstruye la réplica; no solo aumentes disco y lo des por resuelto.

10) ¿Puedo mover binlogs a otro disco sin downtime?

A veces, según versión y configuración, pero planifica ventana de mantenimiento. El movimiento operativo seguro es: parar MySQL, mover archivos, ajustar config (log_bin_basename path), arrancar MySQL y validar con SHOW BINARY LOGS. Si necesitas “sin downtime”, diseña la topología (failover) en lugar de trucos de sistema de archivos.

Conclusión: acciones siguientes que realmente evitan repetirlo

Si recuerdas una cosa: los binlogs no “se descontrolan”. Hacen exactamente lo que pediste—registrar cambios para siempre—hasta que el disco hace que dejes de pedirlo.

Pasos prácticos a seguir:

  1. Establece retención explícita (segundos/días) y confirma que coincide con tu versión y que realmente purga.
  2. Haz de la salud de replicación un SLO de primera clase; una réplica rota no es un accesorio inocuo, es una responsabilidad de disco.
  3. Alinea la retención de binlogs con los backups y realiza drills de PITR para saber qué compras con esos bytes.
  4. Instrumenta la tasa de crecimiento y alerta por “GB por hora”, no solo por “disco al 90%”.
  5. Reduce la amplificación de escritura en jobs masivos: segmenta, indexa, programa y evita transacciones gigantes.

Haz esto y los binlogs volverán a ser lo que deben ser: una red de seguridad silenciosa y útil, no una antología de terror temática sobre almacenamiento.

← Anterior
Google Glass: cuando el futuro se sintió incómodo en público
Siguiente →
Ubuntu 24.04: MySQL «Too many connections» — solución sin ralentizar la BD

Deja un comentario