La primera vez que lo notas, parece red de red. Las páginas se “cuelgan” de forma aleatoria, las llamadas a la API agotan el tiempo y la media de carga de tu VPS hace una convincente impresión de arte moderno.
Luego miras con más detalle y te das cuenta de que la base de datos hace lo que hacen las bases de datos: convierte en silencio pecados menores de configuración en grandes latencias.
En un VPS pequeño, “predeterminado” no es neutro. Predeterminado es un conjunto de suposiciones hechas para la máquina de otra persona: distinto RAM, distinto almacenamiento, distinta carga de trabajo, distinta tolerancia al riesgo.
Elegir MySQL o MariaDB por inercia del gestor de paquetes puede salir caro en IOPS, presión de memoria y sorpresas en la replicación.
La tesis: los predeterminados perjudican a las máquinas pequeñas
En un VPS, la base de datos no es solo otro proceso. Es el proceso que convierte ciclos de CPU en esperas visibles para el usuario.
Si tienes 1–4 vCPU con RAM modesta y un backend de almacenamiento compartido, la diferencia entre “bien” y “dolor” suele ser
un único predeterminado: el tamaño del buffer pool, ajustes de durabilidad, comportamiento de fsync, o un hilo en segundo plano que es correcto para un servidor potente
pero castigador en uno pequeño.
MySQL y MariaDB son lo suficientemente parecidos como para que la gente trate la elección como escoger una marca de agua embotellada. Pero los internos se han ido separando
durante años, y los predeterminados de empaquetado (especialmente en builds de las distribuciones) pueden ponerte en problemas en cámara lenta:
- Buffer pool de InnoDB demasiado pequeño → lecturas constantes, IOPS elevados y latencia en cola.
- Vaciamiento demasiado ansioso → picos en la profundidad de la cola de almacenamiento, bloqueos y tiempos de espera “misteriosos”.
- Modos SQL u optimizador incorrectos → planes que parecen bien hasta que la tabla crece.
- Asumir “reemplazo compatible” para replicación o herramientas → migraciones dolorosas en el peor momento.
La forma correcta de elegir es aburrida: decidir según compatibilidad operativa, rendimiento predecible en tu carga y la ruta de actualización.
Si tienes un VPS típico ejecutando una app web con tablas InnoDB, seré franco: elige el motor que puedas actualizar con confianza y tunear correctamente,
y luego haz tuyos los predeterminados. El enemigo no es MySQL ni MariaDB. El enemigo es dejar que el paquete decida tu postura en producción.
Hechos e historia que importan en producción
Algunos hechos concretos y puntos de contexto que explican por qué “son básicamente lo mismo” dejó de ser verdad:
- MariaDB se bifurcó de MySQL en 2009 tras preocupaciones por la adquisición de Sun por parte de Oracle. La intención fue continuidad; la realidad fue divergencia.
- MySQL 8.0 eliminó la query cache (era un imán de mutex global). MariaDB mantuvo una variante más tiempo, y muchas distros aún incluyen configuraciones que la mencionan.
- MySQL 8.0 usa un diccionario de datos transaccional dentro de InnoDB. MariaDB implementó la metadata de forma diferente. Esto afecta el comportamiento de upgrade, la sensación de recuperación tras un fallo y las suposiciones de las herramientas.
- MariaDB introdujo Aria como reemplazo de MyISAM para tablas temporales internas y seguridad ante caídas. Puede ser útil, pero es otra pieza que se mueve.
- MySQL añadió “instant ADD COLUMN” (en muchos casos) y mejoró el DDL online con el tiempo; MariaDB tiene su propia historia de DDL online y casos límite.
- Las implementaciones GTID difieren entre MySQL y MariaDB. Comparten la sigla, no la compatibilidad completa. Esto importa cuando intentas hacer failover a las 2 a. m.
- Los plugins de autenticación por defecto divergen (notablemente desde MySQL 8). Tus drivers y clientes legados pueden importarlo más de lo que crees.
- Ambos optimizan para throughput, no para tu VPS—lo que significa que el vaciado en segundo plano y la concurrencia de hilos pueden superar benchmarks mientras hacen que la latencia sea irregular en almacenamiento limitado.
Una cita que sigue siendo el mejor resumen de por qué esto importa:
paraphrased idea
— Werner Vogels: todo falla tarde o temprano, así que diseñas y operas suponiendo fallos, no esperando que no ocurran.
Qué realmente ralentiza una base de datos en VPS
“La base de datos está lenta” no es un diagnóstico. En un VPS suele ser una (o varias) de estas categorías:
1) Latencia de almacenamiento y comportamiento de fsync (el asesino silencioso)
El almacenamiento de VPS a menudo está respaldado por red, sobresuscrito o agresivamente cacheado. Tu benchmark puede mostrar 20k IOPS, pero la base de datos necesita
latencia consistente, no números heroicos y con picos. InnoDB es una máquina de durabilidad: escribe redo, hace flush y espera un fsync estable.
Si la latencia de fsync sube, las transacciones se acumulan, los hilos se bloquean y tu app empieza a culpar “a la base de datos” como si fuera un ente consciente.
Lo peor: tu CPU puede estar ociosa mientras todo espera al almacenamiento. Verás CPU baja, alta media de carga y una cola de hilos en “waiting for handler commit”.
2) Presión de memoria: buffer pool pequeño + lucha con la cache de páginas de Linux
En un VPS pequeño no tienes RAM para desperdiciar. Si tu buffer pool de InnoDB es demasiado pequeño, churnearás páginas y golpearás el disco constantemente.
Si es demasiado grande, el SO empezará a reclamar agresivamente, tu máquina hará swap y ahora todo será lento, incluidos los logs de la base de datos, porque el kernel está de mal humor.
El buffer pool “correcto” no es un mantra como “ponlo al 80%”. Depende de qué más se ejecuta en la máquina, cuán grande es tu working set
y si valoras latencia estable sobre la tasa máxima de aciertos en caché.
3) Predeterminados de concurrencia que no coinciden con el número de vCPU
Tanto MySQL como MariaDB pueden crear hilos en segundo plano y comportamientos de concurrencia que van bien en 16 núcleos pero son absurdos en 2.
Si ves contención de mutex, alto cambio de contexto o sobrecarga de planificación de hilos, el cuello de botella no es SQL. Es la coreografía de recursos.
4) Planes de consulta que “funcionaban ayer”
En conjuntos de datos pequeños, un mal indexado se oculta por la caché. En un VPS, en el momento en que el conjunto deja de caber en RAM,
un índice compuesto faltante se convierte en I/O aleatorio y un log de consultas lentas lleno de vergüenza.
Las diferencias en el comportamiento del optimizador entre MySQL y MariaDB pueden cambiar qué consultas caen por el precipicio primero.
Broma #1: Si no puedes reproducir la ralentización en staging, felicidades—has creado un entorno de staging que es excelente mintiendo.
Diferencias prácticas: MySQL 8 vs MariaDB hoy
Hablemos de elecciones que cambian resultados en un VPS, no de debates ideológicos.
El empaquetado y la configuración “por defecto” es la mitad de la batalla
En familias Debian/Ubuntu, instalar mysql-server puede darte MariaDB dependiendo de la distro y la versión.
En algunas distros, MariaDB se distribuye con fragmentos de configuración que incluyen perillas legadas o predeterminados conservadores.
Los paquetes de la comunidad de MySQL a menudo tienen sus propios predeterminados que suponen que los vas a ajustar según tu hardware.
El patrón peligroso: crees que elegiste “MySQL”, pero en realidad elegiste “lo que los mantenedores de la distro probaron para compatibilidad general”.
Eso puede ser correcto para “arranca”, y equivocado para “permanece rápido”.
Compatibilidad de replicación y herramientas operativas
Si replicas, la elección importa más.
MariaDB tiene características que MySQL no tiene, y viceversa. Pero la gran trampa es que los conjuntos GTID y las semánticas no son intercambiables.
Si planeas cambiar de motor más tarde, te estás comprometiendo con un proyecto de migración, no con una actualización de paquete.
Para un VPS de nodo único, la replicación puede existir igual (réplicas de lectura, réplicas de migración, DR).
Si alguna vez quieres crear una réplica rápidamente, “GTID y formatos de binlog compatibles” no es un detalle bonito.
El rendimiento no es “cuál es más rápido”, es “cuál es más predecible”
Cualquiera puede ser rápido. Ambos pueden ser lentos. La pregunta práctica es: ¿cuál te da el comportamiento menos sorprendente bajo presión,
con las semánticas de actualización y configuración menos confusas, en el SO que usas?
- Si necesitas funciones de MySQL 8 (comportamiento moderno de JSON, cambios en el diccionario de datos, compatibilidad del ecosistema), elige MySQL y tunéalo correctamente.
- Si estás en una distro que trata a MariaDB como predeterminado y tu stack ya es compatible, MariaDB está bien—solo audita los predeterminados y elimina la cruft legada.
- Si necesitas moverte entre servicios gestionados y self-hosting, la compatibilidad con MySQL puede reducir fricción. Pero no lo des por hecho; verifica con tus drivers y modos SQL.
Guía rápida de diagnóstico (primero/segundo/tercero)
Esto es lo que haces cuando el VPS está lento y necesitas respuestas antes de que tus usuarios empiecen a escribir poesía en tu bandeja de soporte.
Primero: demuestra si estás ligado por CPU, I/O o memoria
- Revisa espera de almacenamiento y cola de ejecución:
iostat,vmstat. - Revisa presión de memoria y swap:
free,sar(si está disponible). - Revisa estados de hilos de MySQL/MariaDB: processlist / performance schema / contadores de estado.
Segundo: identifica la espera principal dentro de la base de datos
- Métricas de InnoDB: tasa de aciertos del buffer pool, esperas de log, comportamiento de edad de checkpoints.
- Consultas lentas: habilita el log de consultas lentas brevemente si es seguro, o usa los logs existentes.
- Bloqueos: busca transacciones esperando locks de fila o locks de metadata.
Tercero: decide si la solución es consulta/índice, configuración o almacenamiento
- Si el await de disco es alto y el buffer pool es diminuto: ajusta memoria y vaciado primero.
- Si el disco está bien pero los tiempos de consulta suben: revisa planes e índices.
- Si todo parece “bien” pero la latencia es irregular: revisa comportamiento de fsync, vaciado en segundo plano y créditos de ráfaga con tu proveedor.
Tareas prácticas: comandos, resultados, decisiones
Estas son tareas reales que puedes ejecutar en un VPS Linux. Cada una incluye el comando, salida de ejemplo, lo que significa y la decisión que tomas.
Ejecútalas como lista de verificación durante un incidente, o como auditoría básica cuando heredas un servidor que “siempre ha sido así”.
Tarea 1: Confirma qué base de datos estás ejecutando realmente
cr0x@server:~$ mysql --version
mysql Ver 15.1 Distrib 10.11.6-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
Qué significa: Eso es MariaDB 10.11, no Oracle MySQL. La gente confunde el cliente y el servidor, pero esta salida suele ser una pista fiable.
Decisión: Deja de decir “MySQL” en los tickets. Si planeas tunear para MySQL o usar funciones específicas, vuelve a comprobar el origen del paquete y las capacidades del servidor.
Tarea 2: Confirma el demonio del servidor y la fuente del paquete
cr0x@server:~$ systemctl status mariadb --no-pager
● mariadb.service - MariaDB 10.11.6 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; preset: enabled)
Active: active (running) since Mon 2025-12-29 08:12:31 UTC; 2h 14min ago
Docs: man:mariadbd(8)
https://mariadb.com/kb/en/library/systemd/
Main PID: 742 (mariadbd)
Status: "Taking your SQL requests now..."
Tasks: 41 (limit: 2275)
Memory: 1.2G
CPU: 38min 12.331s
Qué significa: El nombre del servicio y el binario principal identifican la bifurcación. También fíjate en el uso de memoria: en un VPS pequeño, 1.2G puede ser ya toda la máquina.
Decisión: Si estás justo de memoria, ve inmediatamente a dimensionar el buffer pool y ajustar conexiones/hilos antes de tocar microoptimizaciones de consultas.
Tarea 3: Comprueba RAM, swap y si ya estás perdiendo
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 3.8Gi 3.1Gi 110Mi 62Mi 640Mi 210Mi
Swap: 2.0Gi 1.4Gi 600Mi
Qué significa: Estás usando swap. “available” es mínimo. El kernel está reclamando y la BD se bloqueará de forma impredecible.
Decisión: Reduce la huella de memoria (buffer pool demasiado grande, demasiadas conexiones, tablas temporales grandes) o aumenta la RAM. Ajustar sin arreglar el swap es postureo de rendimiento.
Tarea 4: Determina si ahora mismo estás limitado por I/O
cr0x@server:~$ iostat -x 1 3
Linux 6.1.0-18-amd64 (server) 12/29/2025 _x86_64_ (2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.00 0.00 6.00 28.00 0.50 53.50
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await
vda 110.0 240.0 4200.0 9200.0 0.0 2.0 96.0 18.7
Qué significa: %util cerca de 100 y await ~19ms: el almacenamiento está saturado y lento. Así se producen los “timeouts” aleatorios.
Decisión: Reduce escrituras (ajustes de flush, sync de binlog, batching de transacciones), aumenta la tasa de aciertos en caché o muévete a almacenamiento más rápido. También revisa límites de ráfaga del proveedor.
Tarea 5: Comprueba si tu sistema de ficheros está lleno o casi lleno
cr0x@server:~$ df -h /var/lib/mysql
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 46G 2.0G 96% /
Qué significa: 96% usado no es “aceptable”. InnoDB necesita espacio para ficheros temporales, logs y operaciones internas.
Decisión: Libera espacio ahora (logs, backups, binlogs antiguos) o amplía disco. No esperes al “disco lleno” y a dramas cercanos a la corrupción.
Tarea 6: Identifica el directorio de datos y comprueba el contexto de doublewrite/redo
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'datadir'; SHOW VARIABLES LIKE 'innodb_doublewrite'; SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';"
+---------------+---------------------+
| Variable_name | Value |
+---------------+---------------------+
| datadir | /var/lib/mysql/ |
+---------------+---------------------+
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| innodb_doublewrite| ON |
+-------------------+-------+
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
Qué significa: La durabilidad está en los predeterminados más seguros. En almacenamiento barato de VPS, esto puede traducirse en muchas esperas de fsync.
Decisión: Para datos realmente críticos, manténlo. Para sistemas “podemos reproducir desde una cola”, considera cambiar innodb_flush_log_at_trx_commit a 2, pero solo aceptando explícitamente el riesgo.
Tarea 7: Comprueba el tamaño del buffer pool respecto a la RAM
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
+-------------------------+------------+
| Variable_name | Value |
+-------------------------+------------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+------------+
Qué significa: Buffer pool de 128MiB en un servidor de 4GiB casi seguro que es demasiado pequeño a menos que el conjunto de datos sea diminuto.
Decisión: Aumentarlo con criterio (a menudo 1–2GiB en un VPS de 4GiB, según servicios co-ubicados). Luego vigila swap y riesgo de OOM.
Tarea 8: Comprueba eficiencia del buffer pool y tasa de lecturas desde disco
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+-----------+
| Variable_name | Value |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_requests | 982334112 |
| Innodb_buffer_pool_reads | 18399244 |
+---------------------------------------+-----------+
Qué significa: Algunas lecturas desde disco son normales. Pero si Innodb_buffer_pool_reads sube rápidamente durante picos, estás perdiendo caché o tienes índices deficientes.
Decisión: Si las lecturas desde disco se correlacionan con picos de latencia, aumenta el buffer pool (si la RAM lo permite) y/o corrige índices. No adivines: correlaciona con ventanas de tiempo.
Tarea 9: Mira en qué cree el servidor que está esperando (triage rápido)
cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST;" | head -n 15
Id User Host db Command Time State Info
112 app 10.0.1.25:53142 prod Query 12 Waiting for handler commit UPDATE orders SET status='paid' WHERE id=?
118 app 10.0.1.25:53159 prod Query 11 Waiting for handler commit INSERT INTO payments(order_id, ...) VALUES (...)
121 app 10.0.1.25:53168 prod Query 9 Waiting for handler commit UPDATE inventory SET ...
130 app 10.0.1.25:53201 prod Sleep 55 NULL
Qué significa: “Waiting for handler commit” suele apuntar a presión de flush/fsync del redo log (durabilidad + latencia de almacenamiento).
Decisión: Enfócate en la ruta de disco/fsync: dimensionado de redo log, política de flush, rendimiento de almacenamiento, ajustes de sync de binlog.
Tarea 10: Comprueba la configuración del log de consultas lentas (y actívalo brevemente si es necesario)
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'slow_query_log_file'; SHOW VARIABLES LIKE 'long_query_time';"
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | OFF |
+----------------+-------+
+---------------------+--------------------------+
| Variable_name | Value |
+---------------------+--------------------------+
| slow_query_log_file | /var/log/mysql/slow.log |
+---------------------+--------------------------+
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| long_query_time | 10.000000 |
+-----------------+-------+
Qué significa: Está desactivado y long_query_time es alto. En un VPS, una consulta de 1–2s ya puede arruinar la latencia en cola.
Decisión: Habilítalo temporalmente con un umbral más bajo durante una ventana controlada, y luego apágalo o rota agresivamente para evitar overhead de I/O.
Tarea 11: Verifica el binlog y ajustes de sincronización (auditoría de amplificación de escritura)
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'sync_binlog'; SHOW VARIABLES LIKE 'binlog_format';"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog | 1 |
+---------------+-------+
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
Qué significa: Esto es seguro pero caro. sync_binlog=1 añade presión de fsync. En almacenamiento frágil, esto puede dominar.
Decisión: Si no replicas y no necesitas recuperación punto en el tiempo con binlogs, considera desactivar binlog. Si lo necesitas, considera sync_binlog=100 (compensando riesgo) y documenta el radio de impacto.
Tarea 12: Comprueba comportamiento de tablas temporales (uso oculto de disco)
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%'; SHOW VARIABLES LIKE 'tmp_table_size'; SHOW VARIABLES LIKE 'max_heap_table_size';"
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Created_tmp_disk_tables | 182331 |
| Created_tmp_files | 44219 |
| Created_tmp_tables | 903112 |
+-------------------------+---------+
+----------------+----------+
| Variable_name | Value |
+----------------+----------+
| tmp_table_size | 16777216 |
+----------------+----------+
+---------------------+----------+
| Variable_name | Value |
+---------------------+----------+
| max_heap_table_size | 16777216 |
+---------------------+----------+
Qué significa: Muchas tablas temporales se desbordan a disco, y los límites son pequeños (16MiB). Trabajos de ordenación/agregación golpearán el almacenamiento.
Decisión: Aumenta esto con cuidado (son límites potenciales por conexión), y corrige consultas que crean tablas temporales enormes. En VPS, las tablas temporales en disco son minas de latencia.
Tarea 13: Comprueba el conteo de conexiones y si estás thrashing de hilos
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW VARIABLES LIKE 'max_connections';"
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 312 |
+-------------------+-------+
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| Threads_running | 46 |
+-----------------+-------+
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 500 |
+-----------------+-------+
Qué significa: 312 conexiones en un VPS pequeño suelen ser autolesivas. Aunque estén durmiendo, la sobrecarga de memoria y gestión de hilos suma.
Decisión: Arregla el pooling en la capa de aplicación, baja max_connections y dimensiona el thread cache adecuadamente. Si necesitas cientos de conexiones, probablemente necesites una máquina más grande o una capa proxy.
Tarea 14: Comprueba la configuración de logs de InnoDB (presión de checkpoint vs tiempo de recuperación)
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'innodb_log_file_size'; SHOW VARIABLES LIKE 'innodb_log_files_in_group';"
+---------------------+----------+
| Variable_name | Value |
+---------------------+----------+
| innodb_log_file_size| 50331648 |
+---------------------+----------+
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_log_files_in_group| 2 |
+--------------------------+-------+
Qué significa: Redo logs que suman ~96MiB son pequeños para cargas de escritura intensiva. Eso puede forzar checkpoints frecuentes y más vaciado en segundo plano.
Decisión: Aumenta el tamaño de log si escribes mucho y el almacenamiento es inestable, equilibrando el tiempo de recuperación. Planifica el cambio correctamente (requiere reinicio y manejo de archivos).
Tarea 15: Confirma los archivos de configuración reales en uso (no ajustes el archivo equivocado)
cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'pid_file'; SHOW VARIABLES LIKE 'log_error';"
+---------------+------------------------+
| Variable_name | Value |
+---------------+------------------------+
| pid_file | /run/mysqld/mysqld.pid |
+---------------+------------------------+
+---------------+--------------------------+
| Variable_name | Value |
+---------------+--------------------------+
| log_error | /var/log/mysql/error.log |
+---------------+--------------------------+
Qué significa: Ahora sabes dónde mirar diagnósticos de arranque y puedes confirmar qué instancia está en ejecución.
Decisión: Abre el log de errores y confirma que no hay advertencias sobre variables ignoradas (común al copiar configuraciones entre MySQL y MariaDB).
Tarea 16: Inspecciona el log de errores en busca de settings ignorados o pistas de recuperación de crash
cr0x@server:~$ sudo tail -n 40 /var/log/mysql/error.log
2025-12-29 08:12:30 0 [Note] mariadbd: ready for connections.
2025-12-29 09:41:02 0 [Warning] 'query_cache_size' is deprecated and will be removed in a future release.
2025-12-29 10:07:11 0 [Warning] Aborted connection 118 to db: 'prod' user: 'app' host: '10.0.1.25' (Got timeout reading communication packets)
Qué significa: Perillas obsoletas pueden seguir funcionando hoy pero indican deriva de configuración. Conexiones abortadas pueden ser timeouts de la app o bloqueos del servidor.
Decisión: Elimina settings depreciados y trata las conexiones abortadas como síntoma: correlaciónalas con await de almacenamiento y paradas de consultas.
Tres microhistorias corporativas (de las que no publicas en Slack)
Microhistoria 1: El incidente causado por una suposición errónea
Una compañía ejecutaba un conjunto de nodos VPS regionales pequeños para cargas sensibles a la latencia. La capa de bases de datos era “MySQL” en el vocabulario de todos.
Se aprovisionaron nodos nuevos desde una imagen de la distro y nadie lo cuestionó. ¿Por qué iban a hacerlo? La app se conectó, las tablas existían, la replicación “funcionaba”.
Durante una ventana de mantenimiento, añadieron una réplica en otra región y planearon un ejercicio rápido de failover.
El ensayo falló de una manera aburrida pero catastrófica: las herramientas basadas en GTID esperaban semánticas de MySQL y reportaron estado conflictivo.
El ingeniero on-call intentó “repararlo” con comandos que eran correctos para un motor y sutilmente incorrectos para el otro.
El resultado no fue pérdida inmediata de datos. Fue peor: un failover parcial con una aplicación que creyó escribir en el primario,
mientras un subconjunto del tráfico alcanzó un nodo que no replicaba limpiamente. El incidente duró lo suficiente como para que aparecieran inconsistencias visibles para clientes.
El postmortem tuvo una línea importante: la suposición de que “MariaDB es un reemplazo drop-in de MySQL” se trató como hecho,
así que nadie documentó qué motor se había desplegado realmente. Esa suposición se propagó a la automatización, umbrales de monitorización y runbooks.
La solución fue aburrida y efectiva: inventariar cada nodo, estandarizar motor/versión por entorno y añadir una comprobación de arranque en la canalización de despliegue
que verifique el flavor del servidor esperado y variables críticas. Dejaron de depender de impresiones y empezaron a depender de hechos.
Microhistoria 2: La optimización que salió mal
Otro equipo persiguió latencia de escritura en una plataforma VPS económica. Leyeron sobre perillas de durabilidad y decidieron “acelerar las cosas”
relajando el comportamiento de flush en commit. El cambio fue pequeño, los gráficos mejoraron y el equipo celebró en silencio—porque las celebraciones ruidosas tientan a la suerte.
Dos semanas después, el host tuvo un reboot no planificado. La base de datos se recuperó, la app volvió y todos pensaron que lo habían sorteado.
Luego la cola de soporte empezó a llenarse con informes de actualizaciones recientes que faltaban. No eran volúmenes enormes, pero sí reales.
El sistema se comportó técnicamente como se configuró: las transacciones confirmadas a los clientes no estaban necesariamente en almacenamiento estable en el momento del acuse.
Eso es exactamente lo que habían pedido. El problema no fue la perilla; el problema fue que cambiaron el contrato de durabilidad sin cambiar las expectativas del producto.
El rollback fue inmediato. La “optimización” a largo plazo se convirtió en un plan de ingeniería real:
mantener durabilidad estricta para tablas núcleo, enviar escrituras menos críticas por una cola con idempotencia,
y dejar de usar perillas de durabilidad de la base de datos como sustituto de arquitectura.
Microhistoria 3: La práctica aburrida pero correcta que salvó el día
Un equipo SaaS ejecutaba MariaDB en VPS modestos. Nada sofisticado. Pero tenían un hábito: simulacros semanales de restauración.
No restauraciones teóricas. Restauraciones reales a un VPS de prueba, con smoke tests de la app y una validación básica por checksum.
Un día, un desarrollador desplegó una migración que introdujo un patrón de consulta que creó enormes tablas temporales en disco.
La base de datos no “colapsó”. Simplemente se volvió cada vez más lenta a medida que el disco se llenaba. Eventualmente alcanzó 100% y el servicio cayó.
La respuesta inmediata fue desordenada: liberar espacio, matar las peores consultas, revertir la app. Pero algunas tablas quedaron parcialmente actualizadas
cuando se llenó el disco y la lógica de rollback no estaba limpia. Necesitaron restaurar a un punto antes de la migración.
La práctica de restauración dio sus frutos. El equipo ya conocía los pasos de restauración, el sobre de tiempo de recuperación y el archivo de configuración
que siempre se olvidaba. Restauraron, validaron, reprodujeron solo el subconjunto seguro de cambios y volvieron en línea con mínimo drama.
La acción final no fue “ten más cuidado”. Fue concreta: añadir comprobaciones de plan de consulta para la ruta de migración, monitorizar tasas de spill de tablas temporales,
y seguir haciendo los aburridos simulacros de restauración que todos critican hasta que te salvan el fin de semana.
Errores comunes: síntoma → causa raíz → arreglo
1) Síntoma: CPU baja, alta media de carga, timeouts aleatorios
Causa raíz: espera de I/O y saturación de almacenamiento; hilos bloqueados en fsync/flush o lecturas lentas.
Arreglo: Usa iostat -x para confirmar alto await/%util. Aumenta buffer pool si está subdimensionado, reduce amplificación de escritura (sync de binlog, política de flush) y considera mover a mejor almacenamiento.
2) Síntoma: el rendimiento se hunde tras un pequeño aumento de tráfico
Causa raíz: el working set ya no cabe en el buffer pool; ahora haces lecturas aleatorias desde disco.
Arreglo: Mide lecturas del buffer pool vs solicitudes. Añade índices faltantes, aumenta buffer pool y verifica que no estés haciendo swap después.
3) Síntoma: paradas súbitas durante ráfagas intensas de escritura
Causa raíz: presión de checkpoint (redo logs demasiado pequeños) o vaciado agresivo interactuando con almacenamiento lento.
Arreglo: Aumenta capacidad de redo log donde corresponda y ajusta comportamiento de flush. Valida con estados de processlist como “Waiting for handler commit”.
4) Síntoma: el uso de memoria crece hasta que la máquina hace swap
Causa raíz: demasiadas conexiones, buffers grandes por conexión, crecimiento de tablas temporales o buffer pool fijado demasiado alto para la máquina.
Arreglo: Limita conexiones, implementa pooling, reduce buffers por conexión y ajusta el buffer pool al tamaño correcto. Verifica con free -h y Threads_connected.
5) Síntoma: herramientas de replicación/failover se comportan de forma extraña
Causa raíz: asumir que las semánticas GTID/replicación de MySQL y MariaDB coinciden; versiones/sabores mixtos.
Arreglo: Estandariza el motor en toda la topología o usa planes de compatibilidad explícitos. No mezcles sin un runbook probado.
6) Síntoma: “Se volvió más lento después de que actualizamos”
Causa raíz: cambio en comportamiento del optimizador, modos SQL o variables de configuración ya no válidas; también posible regresión por predeterminados diferentes.
Arreglo: Haz diff de variables clave antes/después de la actualización, inspecciona el log de errores por settings ignorados y re-baselina con muestras de consultas lentas y planes EXPLAIN.
Broma #2: La base de datos no se puso lenta “aleatoriamente”. Solo está expresando sus sentimientos sobre tus predeterminados.
Listas de verificación / plan paso a paso
Plan A: Tienes un VPS lento ahora mismo
- Estabiliza: confirma espacio en disco (
df -h), para consultas descontroladas y asegúrate de no estar haciendo swap intensamente. - Clasifica: ejecuta
iostat -xyfree -h. Decide: I/O-bound vs memory-bound vs CPU-bound. - Principales esperas: processlist por commits/locks; muestreo rápido del slow log si es seguro.
- Afinamiento rápido: arregla buffer pool si es minúsculo; reduce conexiones; aborda spills de tablas temporales.
- Perillas de alto riesgo: cambia la durabilidad solo con aprobación explícita y pasos de rollback.
- Valida: mide latencia p95 y await de almacenamiento tras los cambios, no solo QPS promedio.
Plan B: Estás eligiendo entre MySQL y MariaDB para un nuevo VPS
- Decide según necesidades del ecosistema: drivers cliente, rarezas de ORMs, alineación con servicios gestionados, necesidades de replicación.
- Elige un motor por entorno: no mezcles “porque funcionó una vez”.
- Configs base: establece buffer pool, tamaños de logs, límites de conexiones y política del slow log de forma intencional.
- Planifica upgrades: prueba upgrades menores y validez de configuración en staging que refleje lo más posible las características de almacenamiento del VPS.
- Construye observabilidad: captura slow query logs (rotados), métricas básicas de OS y contadores de estado de BD.
Plan C: Ya ejecutas uno y quieres cambiar
- No lo trates como un swap de paquete: trátalo como una migración con pruebas de compatibilidad.
- Inventario de características: modos SQL, plugins de autenticación, formato de replicación, rutinas almacenadas, triggers y comportamiento de charset/collation.
- Dry run de restore/migración: practica export/import o enfoques de replicación lógica en un nodo de prueba.
- Corte con rollback: define criterios exactos de rollback (tasas de error, lag de replicación, checks de datos) y mantiene el antiguo primario read-only por una ventana.
Preguntas frecuentes
1) ¿MariaDB es más rápido que MySQL en un VPS?
A veces, para cargas y versiones específicas. Pero en VPS el determinante mayor es si tus predeterminados coinciden con la RAM y la latencia de almacenamiento.
Cualquiera de los dos puede ser rápido; cualquiera de los dos puede ser un desastre por paradas.
2) ¿Por qué la “instalación por defecto” suele sentirse lenta en servidores pequeños?
Los predeterminados buscan compatibilidad amplia y durabilidad. En almacenamiento restringido, un comportamiento de fsync seguro y caches pequeños pueden traducirse en paradas frecuentes.
Necesitas ajustar la memoria y reducir la amplificación de escritura innecesaria.
3) ¿Puedo tunear para compensar un almacenamiento VPS lento?
Puedes reducir el daño (buffer pool más grande, menos fsyncs, menos tablas temporales en disco). Pero no puedes tunear la latencia de almacenamiento terrible.
Si iostat muestra alto await con carga modesta, actualizar el almacenamiento o cambiar de proveedor puede ser la solución real.
4) ¿Debería cambiar innodb_flush_log_at_trx_commit a 2?
Solo si entiendes y aceptas el compromiso de durabilidad: puedes perder hasta aproximadamente un segundo de transacciones en un crash.
Para muchas apps web con colas e idempotencia, es aceptable. Para sistemas financieros o registros oficiales, normalmente no lo es.
5) ¿Es seguro desactivar el binary logging?
Si no replicas y no usas binlogs para recuperación punto en el tiempo, desactivarlo puede reducir carga de escritura.
Si necesitas PITR o réplicas, mantenlo activado y ajusta sync_binlog documentando el riesgo.
6) ¿Por qué hay tantas conexiones “Sleeping” en el processlist?
Típicamente es pooling de la aplicación o conexiones filtradas. Sleeping no es gratis: la memoria y la gestión de hilos suman.
Arregla el pooling y limita max_connections para forzar disciplina.
7) ¿Puedo mezclar un primario MySQL con réplicas MariaDB (o al revés)?
A veces en configuraciones limitadas, pero es frágil y dependiente de la versión. Para producción, asume “no” a menos que tengas un plan de compatibilidad probado, documentado
y validado continuamente. Aquí no quieres sorpresas.
8) ¿Cuál es la única perilla mejor para rendimiento en VPS?
innodb_buffer_pool_size dimensionado correctamente para tu RAM y carga, combinado con mantener la máquina fuera de swap.
Después de eso, la mejor perilla suele ser “añadir el índice correcto”.
9) ¿Cómo sé si mi problema son consultas o configuración?
Si iostat muestra alto await y las lecturas de InnoDB aumentan durante picos, probablemente estés limitado por caché/índices.
Si el processlist muestra esperas de commit y el binlog sync es estricto, probablemente estés limitado por fsync/amplificación de escritura.
10) ¿Debo usar MyISAM/Aria para velocidad en un VPS?
Para tablas de aplicación: casi siempre no. La seguridad ante caídas y el comportamiento de concurrencia de InnoDB son lo que quieres.
Cambiar de motor por “velocidad” suele solo desplazar el dolor hacia la recuperación e integridad de datos.
Siguientes pasos que puedes hacer esta semana
Si tu base de datos en VPS está lenta, no empieces con religión de motor. Empieza con medición y luego elimina las heridas autoinfligidas obvias.
- Identifica el motor y la versión que realmente ejecutas y documenta en tu runbook.
- Baselina la realidad del SO: captura
free -h,df -hyiostat -xdurante periodos normal y pico. - Dimensiona correctamente el InnoDB buffer pool y limita el número de conexiones para dejar de pelear con el kernel.
- Audita la amplificación de escritura (binlog, ajustes de sync, flush en commit) y decide explícitamente tu contrato de durabilidad.
- Activa el log de consultas lentas brevemente (con rotación) y arregla los principales culpables con índices y cambios de consulta.
- Practica una restauración a un VPS de prueba. No porque sea divertido. Porque algún día será necesario y querrás memoria muscular.
Elige MySQL o MariaDB según compatibilidad y tu ruta de actualización. Luego haz tuyos los predeterminados.
Tu VPS dejará de “ralentizarse aleatoriamente”. Se volverá predeciblemente rápido, que es el único tipo de rápido que importa en producción.