MySQL vs MariaDB en NVMe vs SSD SATA: por qué tu BD sigue lenta (y cómo demostrarlo)

¿Te fue útil?

Compraste NVMe. Migraste el directorio de datos. Publicaste el mensaje “ahora somos modernos” en el canal del equipo.
Y la aplicación sigue expirando a las 9:05 a. m. como si estuviera en un disco duro de portátil de 2007.

Aquí está la verdad incómoda: las mejoras de almacenamiento no solucionan los problemas de rendimiento de bases de datos a menos que entiendas qué tipo de E/S estás ejecutando realmente.
“NVMe es rápido” no es un diagnóstico. Es una orden de compra.

Qué está realmente lento: latencia, fsync, CPU, bloqueos o la consulta

Cuando alguien dice “la base de datos está lenta”, usualmente quiere decir “el usuario está esperando”.
La espera puede ser causada por el disco, claro. Pero con la misma frecuencia es causada por:

  • CPU (mal plan, índice faltante, o simplemente demasiadas solicitudes concurrentes)
  • Bloqueos/transacciones (bloqueos de filas, gap locks, metadata locks o transacciones de larga duración)
  • Latencia en la ruta de commit (fsync, vaciado de redo log, comportamiento de doublewrite)
  • Amplificación de lectura (lecturas aleatorias porque el working set no cabe en memoria)
  • Amplificación de escritura (escrituras pequeñas que disparan gran trabajo en segundo plano)
  • Red y DNS problemáticos (sí, esto aún pasa)

NVMe puede ofrecer un rendimiento increíble. Pero las bases de datos no compran rendimiento; compran latencia predecible.
Un solo pico de fsync en el percentil 99.9 puede arruinar tus gráficas de latencia p95 de la API para toda la mañana.

Mi regla operacional: si no has medido a dónde va el tiempo, no estás solucionando problemas; estás narrando.

Una cita para tener en mente mientras mides: “La esperanza no es una estrategia.” — General Gordon R. Sullivan.

MySQL vs MariaDB: dónde diverge el comportamiento en el mundo real

Son primos, no gemelos

Para la mayoría de los equipos de aplicación, MySQL y MariaDB “se sienten” igual hasta que te llaman a las 02:00 y necesitas interpretar
un volcado de threads, un retraso en la replicación o una regresión de rendimiento tras una actualización menor.
En almacenamiento, ambos dependen fuertemente de la misma física: caches de página, writeback, comportamiento de fsync y la forma de tu carga de trabajo.
Pero sus valores por defecto, características y comportamientos en casos límite pueden diferir lo suficiente como para cambiar tu cuello de botella.

Comportamiento de commit y flushing: lo aburrido que te hace lento

La ruta de commit es donde NVMe recibe más atención: escrituras de log y vaciados.
El problema es que el comportamiento de flush del sistema es una negociación a tres bandas entre:
configuraciones de InnoDB, tu sistema de archivos + opciones de montaje, y el firmware/controlador del dispositivo.

  • innodb_flush_log_at_trx_commit decide cuándo se vacía el redo en el commit (durabilidad vs latencia).
  • sync_binlog influye con qué frecuencia se fsyncea el binlog (seguridad de replicación vs latencia de escritura).
  • innodb_flush_method (a menudo O_DIRECT) afecta doble cacheo y patrones de escritura.
  • Tamaño del redo log y presión de checkpoints pueden convertir un dispositivo decente en una fábrica de jitter.

Diferencias en instrumentación de rendimiento que notarás

MySQL moderno tiene una historia sólida con Performance Schema y un gran ecosistema alrededor. MariaDB también tiene buena instrumentación,
pero encontrarás diferencias en nombres de tablas, contadores y qué esperas están expuestos y en qué forma.
No elijas una base de datos porque un blog dijo que es “más rápida”. Elige según:
claridad operacional, comportamiento de replicación con el que puedas vivir, y la capacidad de tu equipo para depurarlo bajo presión.

Consejo opinado: si no puedes responder “¿cuál es el evento de espera principal en el pico?” en 60 segundos, vuelas a ciegas sin importar la marca.

NVMe vs SATA SSD: las partes que importan para las bases de datos

NVMe no es solo “un SSD más rápido”

El rendimiento de los SSD SATA estuvo moldeado por la interfaz AHCI: profundidad de cola limitada, mayor sobrecarga, y un diseño que tenía sentido cuando
el almacenamiento fingía ser un disco giratorio. NVMe es un modelo diferente: muchas colas paralelas, baja sobrecarga y mucha mayor
IOPS potencial.

Si tu carga es single-threaded, o dominada por latencia de fsync, o bloqueada por bloqueos de filas,
NVMe no se sentirá como magia. Se sentirá como si hubieras mejorado el motor del coche mientras remolcas un tráiler con el freno de mano puesto.

A las bases de datos les importan los detalles feos

  • Distribución de latencia (p99 importa más que “hasta 3.5GB/s”)
  • Comportamiento de la caché de escritura (caché volátil + protección contra pérdida de energía cambia todo)
  • Throttling térmico (los discos NVMe lo hacen silenciosamente, como un saboteador educado)
  • Profundidad de cola y paralelismo (un único hilo ocupado no saturará NVMe)
  • Rendimiento en estado estable (después de que se agote la caché SLC)

Chiste #1: Comprar NVMe para arreglar una consulta lenta es como comprar un ascensor más rápido porque olvidaste dónde estacionaste.
El viaje es más suave, pero sigues perdido.

Cuando SATA puede ser “suficiente”

Si tu conjunto de datos cabe en memoria, las lecturas se sirven mayormente desde el buffer pool y caches del SO. El almacenamiento importa principalmente para:
escrituras de redo log, escrituras de binlog, y flushes de página ocasionales.
Un SSD SATA decente puede manejar eso—hasta que la concurrencia sube y aparecen tormentas de fsync.

NVMe suele brillar cuando tienes alta concurrencia con I/O aleatorio, working sets grandes,
y mucha presión de escritura—o cuando necesitas baja latencia bajo carga, no solo alto rendimiento pico en una diapositiva de marketing.

Hechos interesantes y contexto (lo que la gente olvida)

  1. MariaDB empezó en 2009 como un fork tras la adquisición de Sun Microsystems por Oracle, que era propietaria de MySQL.
  2. InnoDB se convirtió en el motor por defecto para MySQL hace años; no siempre fue la norma—MyISAM solía ser común, con un comportamiento de E/S muy distinto.
  3. NVMe 1.0 se lanzó en 2011, y el mito “NVMe lo hace todo rápido” llegó poco después, a menudo sin pruebas conscientes de la profundidad de cola.
  4. Las semánticas de fsync varían según el sistema de archivos y las opciones de montaje; dos servidores con SSD idénticos pueden mostrar latencias de commit muy diferentes.
  5. La amplificación de escritura es real: un único cambio lógico de página puede desencadenar redo logging, doublewrite buffering, flushing y merges en segundo plano.
  6. Los números IOPS son negociables: los vendedores los cotizan en profundidades de cola y patrones de acceso específicos que rara vez coinciden con OLTP.
  7. Los planificadores de I/O de Linux han cambiado con el tiempo; NVMe a menudo usa un planificador por defecto distinto al de SATA, impactando la latencia bajo carga.
  8. La protección contra pérdida de energía importa: los SSD “enterprise” suelen tener condensadores; los SSD de consumo a menudo no. Esto cambia el caching de escritura seguro y la durabilidad.
  9. La replicación depende del fsync del binlog: empujar la durabilidad hacia configuraciones “rápidas” puede hacer que las réplicas se retrasen de formas distintas y confusas.

Cómo demostrar el cuello de botella: 12+ tareas prácticas con comandos

Estos no son “benchmarks para presumir.” Son sondas. Ejecútalos durante un periodo lento y durante un periodo normal.
Captura la salida. Decide con base en evidencia.

Task 1: Confirma qué almacenamiento estás usando realmente (NVMe vs SATA) y cómo está conectado

cr0x@server:~$ lsblk -d -o NAME,MODEL,TRAN,ROTA,SIZE
NAME  MODEL                 TRAN ROTA  SIZE
nvme0n1 Samsung SSD 990 PRO  nvme    0  1.8T
sda   Samsung SSD 860 EVO    sata    0  1.8T

Qué significa: TRAN te dice la interfaz. ROTA=0 significa “no rotacional” (SSD).
Decisión: si tu datadir todavía está en sda, tu compra de NVMe es actualmente decorativa.

Task 2: Verifica el datadir de MySQL/MariaDB y en qué sistema de archivos vive

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'datadir';"
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| datadir       | /var/lib/mysql/ |
+---------------+-----------------+

cr0x@server:~$ df -Th /var/lib/mysql
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4  1.8T  620G  1.1T  37% /var/lib/mysql

Qué significa: estás en ext4 respaldado por la partición NVMe nvme0n1p2.
Decisión: anota el tipo de sistema de archivos; afecta las semánticas de durabilidad y las opciones de tuning.

Task 3: Revisa las opciones de montaje que influyen en barreras de escritura y latencia

cr0x@server:~$ findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var/lib/mysql
/var/lib/mysql /dev/nvme0n1p2 ext4 rw,relatime,errors=remount-ro

Qué significa: montaje ext4 básico. No se muestran opciones exóticas.
Decisión: si ves rarezas como data=writeback o barreras deshabilitadas, trátalo como una revisión de riesgo, no como un “ajuste de rendimiento”.

Task 4: Confirma el método de flush de InnoDB y las perillas de durabilidad del commit

cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_flush_method','innodb_flush_log_at_trx_commit','sync_binlog');"
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1     |
| innodb_flush_method            | O_DIRECT |
| sync_binlog                    | 1     |
+--------------------------------+-------+

Qué significa: durabilidad completa: vacía redo en cada commit; fsync del binlog en cada transacción.
Decisión: si la latencia es tu problema y puedes tolerar cierto riesgo, podrías considerar relajar una perilla—pero solo después de medir las esperas de fsync (Task 10).

Task 5: Mide la salud del dispositivo y si está haciendo throttling o reportando errores

cr0x@server:~$ sudo smartctl -a /dev/nvme0n1 | sed -n '1,25p'
smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.8.0] (local build)
=== START OF INFORMATION SECTION ===
Model Number:                       Samsung SSD 990 PRO
Serial Number:                      S7X...
Firmware Version:                   5B2QJXD7
PCI Vendor/Subsystem ID:            0x144d
IEEE OUI Identifier:                0x002538
Total NVM Capacity:                 2,000,398,934,016 [2.00 TB]
Unallocated NVM Capacity:           0
Controller ID:                      4
Number of Namespaces:               1
Namespace 1 Size/Capacity:          2,000,398,934,016 [2.00 TB]

Qué significa: el dispositivo es reconocido y proporciona información de firmware.
Decisión: si ves advertencias críticas, errores de medio o temperatura alta, deja de “tunar MySQL” y arregla primero la ruta de hardware.

Task 6: Revisa la temperatura NVMe y señales de throttling térmico

cr0x@server:~$ sudo smartctl -a /dev/nvme0n1 | grep -E "Temperature:|Critical Warning|Thermal"
Critical Warning:                   0x00
Temperature:                        64 Celsius

Qué significa: 64°C es cálido pero no catastrófico para muchas unidades; los umbrales de throttling varían.
Decisión: si las temperaturas suben durante el pico y los picos de latencia coinciden, añade flujo de aire/disipador o mueve el dispositivo; no discutas con la termodinámica.

Task 7: Inspecciona la vista del kernel sobre el planificador de I/O y el encolamiento

cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[mq-deadline] none kyber bfq

cr0x@server:~$ cat /sys/block/sda/queue/scheduler
[mq-deadline] none kyber bfq

Qué significa: mq-deadline está activo.
Decisión: si persigues latencia de cola, la elección del planificador puede importar. Pero cámbialo solo con evidencia de latencia antes/después, no por corazonadas.

Task 8: Valida si estás ligado a CPU durante “problemas de almacenamiento”

cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.8.0 (server)  12/29/2025  _x86_64_ (16 CPU)

12:01:22 PM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:01:23 PM  all   62.1  0.0   9.2    1.1   0.0   0.6    0.0  27.0

Qué significa: bajo iowait, alta CPU de usuario. Eso grita “consultas/cómputo” más que “el disco es lento”.
Decisión: mueve la atención a planes de consulta, contención y eficiencia del buffer pool antes de reemplazar discos otra vez.

Task 9: Confirma la presión a nivel de disco y la latencia con iostat

cr0x@server:~$ iostat -x 1 5
Linux 6.8.0 (server)  12/29/2025  _x86_64_ (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          58.10    0.00    8.90    2.40    0.00   30.60

Device            r/s     rkB/s   rrqm/s  %rrqm  r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm  w_await wareq-sz  aqu-sz  %util
nvme0n1         520.0  68400.0     0.0    0.0    2.10   131.5     880.0  99200.0     0.0    0.0    8.70   112.7     8.3   78.0

Qué significa: write await ~8.7ms y util 78% indica presión significativa; NVMe aún puede estar “ocupado” si tu carga es muy síncrona.
Decisión: correlaciona con esperas de log de InnoDB y tiempo de fsync; si los await se elevan alineados con la latencia de commit, ajusta la ruta de log/checkpoint, no el buffer pool.

Task 10: Identifica clases de espera en MySQL/MariaDB (especialmente I/O y log)

cr0x@server:~$ mysql -e "SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT/1000000000000 AS seconds_waited
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE 'wait/io/file/%' OR EVENT_NAME LIKE 'wait/synch/%'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;"
+------------------------------------------+------------+----------------+
| EVENT_NAME                               | COUNT_STAR | seconds_waited |
+------------------------------------------+------------+----------------+
| wait/io/file/innodb/innodb_log_file      | 1823345    | 913.42         |
| wait/io/file/innodb/innodb_data_file     | 937223     | 411.08         |
| wait/synch/mutex/innodb/log_sys_mutex    | 8234432    | 210.55         |
+------------------------------------------+------------+----------------+

Qué significa: las esperas de archivo de log dominan. Eso a menudo se mapea a latencia de fsync o contención de mutex de log.
Decisión: enfócate en el tamaño del redo log, ajustes de flush y la concurrencia de commit; NVMe por sí solo no solucionará un cuello de botella de log serializado.

Task 11: Revisa la presión de checkpoints de InnoDB y el comportamiento de páginas sucias

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
...
Log sequence number          834992230144
Log flushed up to            834992229120
Last checkpoint at           834991100928
...
Modified db pages            214833
Pending writes: LRU 0, flush list 12, single page 0
...

Qué significa: pendientes en la lista de flush y muchas páginas modificadas pueden indicar que el flushing en segundo plano está luchando.
Decisión: si la edad del checkpoint se mantiene alta y las escrituras pendientes aumentan en el pico, considera logs de redo más grandes y revisa innodb_io_capacity después de medir la capacidad real del dispositivo.

Task 12: Verifica la tasa de aciertos del buffer pool y si las lecturas vienen del disco

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+------------+
| Variable_name                         | Value      |
+---------------------------------------+------------+
| Innodb_buffer_pool_read_requests      | 9823344123 |
| Innodb_buffer_pool_reads              | 92233441   |
+---------------------------------------+------------+

Qué significa: muchas lecturas de disco en relación a las solicitudes puede significar que el working set no cabe en memoria o que las consultas hacen demasiados escaneos.
Decisión: si las lecturas de disco se disparan durante periodos lentos, añade memoria / redimensiona el buffer pool o arregla las consultas que escanean.

Task 13: Confirma el dimensionamiento del redo log (y si es absurdamente pequeño)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'innodb_redo_log_capacity'; SHOW VARIABLES LIKE 'innodb_log_file_size';"
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| innodb_redo_log_capacity| 2147483648 |
+-------------------------+------------+
+-------------------+-----------+
| Variable_name     | Value     |
+-------------------+-----------+
| innodb_log_file_size | 0      |
+-------------------+-----------+

Qué significa: uso de dimensionamiento por capacidad para redo (2GiB aquí). Logs pequeños pueden forzar checkpoints frecuentes; logs enormes pueden ocultar problemas pero aumentar el tiempo de recuperación tras un crash.
Decisión: si las esperas de log dominan y la presión de checkpoints es alta, aumentar la capacidad de redo suele ser una ganancia limpia—verifica primero los objetivos de recuperación.

Task 14: Valida que no estés limitado por espacio libre del sistema de archivos o presión por fragmentación

cr0x@server:~$ df -h /var/lib/mysql
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  1.8T  1.6T  140G  92% /var/lib/mysql

Qué significa: 92% lleno es un olor a problema de rendimiento. Los SSD y los sistemas de archivos se comportan peor cuando están casi llenos; MySQL también puede tener problemas con purge y espacio temporal.
Decisión: espacio libre es rendimiento. Presupuéstalo como presupuestas la CPU.

Task 15: Revisa el comportamiento de spill de tablas temporales (a menudo mal diagnosticado como “el disco es lento”)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%';"
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Created_tmp_disk_tables | 184223  |
| Created_tmp_tables      | 231002  |
+-------------------------+---------+

Qué significa: muchas tablas temporales van al disco. Eso puede castigar tanto a SATA como a NVMe, especialmente si tmpdir está en almacenamiento más lento.
Decisión: identifica las consultas que crean tablas temporales; ajusta índices o la forma de la consulta primero, luego afina tamaños de tablas temporales con cuidado.

Task 16: Observa la replicación por lag inducido por I/O (los stalls de commit pueden propagarse)

cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Seconds_Behind_Master|Slave_SQL_Running|Slave_IO_Running|Relay_Log_Space"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 87
Relay_Log_Space: 2143027200

Qué significa: lag y grandes relay logs pueden ser causados por aplicación lenta debido a presión de fsync, paralelismo insuficiente o lecturas pesadas.
Decisión: si el lag sube durante picos de escritura, trata el almacenamiento + ajustes de durabilidad como parte del rendimiento de replicación, no como un mundo aparte.

Guion de diagnóstico rápido

Esta es la versión “tengo 15 minutos y un manager mirándome por encima del hombro”. No debatir arquitectura. Encuentra el cuello de botella.

Primero: confirma si la base de datos está esperando por E/S o por otra cosa

  • Ejecuta mpstat: si iowait es bajo y la CPU alta, no es “el disco” (Task 8).
  • Ejecuta iostat -x: mira await y %util para tu dispositivo de datadir (Task 9).
  • Revisa los eventos de espera principales en Performance Schema: si I/O de archivo de log domina, estás en problemas en la ruta de commit (Task 10).

Segundo: determina si es presión de lectura, presión de escritura o latencia de commit

  • Presión de lectura: aumento de lecturas del buffer pool (Task 12), muchas lecturas de disco en iostat, consultas lentas mostrando escaneos completos.
  • Presión de escritura: páginas sucias + flushes pendientes + lag de checkpoints en el estado de InnoDB (Task 11).
  • Latencia de commit: esperas de archivo de log y mutex, además de picos de latencia visibles en endpoints de escritura (Task 10 + Task 4).

Tercero: valida la ruta de almacenamiento y elimina la suposición “es NVMe así que está bien”

  • Confirma que el datadir realmente está en NVMe (Task 1–2).
  • Revisa salud/temperatura del dispositivo (Task 5–6).
  • Confirma que el sistema de archivos/opciones de montaje no están haciendo algo sorprendente (Task 3).

Cuarto: decide qué cambiar (y qué no tocar)

  • Si las esperas apuntan a consultas/bloqueos: arregla consultas, índices, ámbito de transacción. No ajustes flags del sistema de archivos para compensar índices faltantes.
  • Si las esperas apuntan a redo/binlog fsync: ajusta dimensionamiento de redo y la política de commit con requisitos de durabilidad claros.
  • Si el dispositivo muestra throttling/errores: arregla hardware, cooling, firmware o la colocación antes de tocar la configuración de BD.

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

1) “Nos mudamos a NVMe pero las escrituras siguen siendo espiky”

Síntoma: p95 de latencia con picos principalmente en endpoints de escritura; la CPU parece estar bien.

Causa raíz: la ruta de commit está serializada por flush de redo log, fsync del binlog o contención del mutex de log; el throughput del dispositivo no ayuda.

Solución: mide las esperas de log (Task 10), verifica las perillas de durabilidad (Task 4), aumenta la capacidad de redo (Task 13) y valida que la política de fsync del binlog coincida con las necesidades del negocio.

2) “Va lento solo durante los reportes”

Síntoma: la carga OLTP se ralentiza cuando corren consultas analíticas/reportes.

Causa raíz: churn del buffer pool + escaneos largos + spills a tablas temporales; el almacenamiento se convierte en la víctima, no el culpable.

Solución: identifica las consultas que escanean más, añade los índices adecuados, descarga los reportes a una réplica y vigila Created_tmp_disk_tables (Task 15).

3) “La espera de I/O es baja pero los usuarios se quejan”

Síntoma: bajo iowait del sistema; los timeouts de la app persisten.

Causa raíz: contención de bloqueos, filas calientes o saturación de CPU. La mejora de almacenamiento no lo tocará.

Solución: inspecciona las esperas (Task 10), revisa el slow query log y la duración de transacciones, reduce la contención (batching, índices, transacciones más pequeñas).

4) “El lag de replicación apareció después de que ‘optimizamos la durabilidad’”

Síntoma: las réplicas quedan atrás durante picos; el maestro parece rápido.

Causa raíz: relajar ajustes de fsync cambió las características de la carga; las réplicas ahora manejan ráfagas de forma diferente, o el patrón I/O de binlog/relay empeoró.

Solución: vuelve a revisar sync_binlog y políticas de commit; mide el crecimiento de relay log y el comportamiento de aplicación (Task 16).

5) “NVMe es rápido, pero el servidor se congela por segundos”

Síntoma: stalls periódicos; las gráficas muestran caídas agudas de latencia.

Causa raíz: throttling térmico, rarezas de firmware del dispositivo o tormentas de writeback del sistema de archivos cuando está casi lleno.

Solución: revisa temperatura y advertencias (Task 6), mantén espacio libre (Task 14) y verifica el comportamiento del kernel/dispositivo durante los stalls.

6) “Hicimos benchmark de 3GB/s, así que producción debería ir bien”

Síntoma: el rendimiento sintético es excelente; la producción no lo es.

Causa raíz: métrica equivocada. OLTP trata sobre lecturas/escrituras pequeñas aleatorias, latencia de fsync y comportamiento en la cola bajo concurrencia.

Solución: usa pruebas con forma de carga real, enfócate en p99 fsync y esperas de log, y compara bajo concurrencia realista (Task 9–11).

Tres microhistorias corporativas desde el terreno

Microhistoria #1: El incidente causado por una suposición equivocada

Una empresa SaaS mediana migró su base de datos primaria de SSD SATA a NVMe durante una ventana de mantenimiento planificada.
Hicieron los pasos básicos bien: copiar datos, actualizar fstab, verificar datadir. El cambio request decía “esperar mejora de I/O 3–5x”.
A la mañana siguiente, soporte se llenó: fallos aleatorios en checkouts, picos extraños en la latencia de pedidos y algunos deadlocks alarmantes.

El equipo on-call supuso que la unidad NVMe estaba defectuosa porque los síntomas eran “tipo I/O”. Ejecutaron una prueba rápida de escritura secuencial,
obtuvieron grandes números y concluyeron “el hardware está bien”. Luego pasaron horas cambiando parámetros del kernel y deshabilitando cosas que no entendían.
La latencia empeoró. Naturalmente.

El problema real no era el dispositivo. Era una tabla caliente única con un patrón de transacciones de larga duración introducido la semana anterior.
NVMe hizo algunas partes más rápidas, lo que aumentó la concurrencia, lo que aumentó la contención de bloqueos, lo que elevó la latencia cola.
El cambio de almacenamiento coincidió con el incidente, así que se llevó la culpa. A los humanos les encanta una historia ordenada.

Cuando finalmente revisaron las esperas de Performance Schema, el evento principal no era I/O de disco. Eran esperas por bloqueos y un wait de mutex de log causado por
mucha contención en commits. Arreglaron el ámbito de transacción de la aplicación, añadieron un índice que eliminó un escaneo, y el “incidente NVMe” desapareció.
La lección del postmortem no fue “no compren NVMe”. Fue “no promuevas correlación a causalidad solo porque queda bien en un mensaje de Slack”.

Microhistoria #2: La optimización que salió mal

Otra organización tenía un clúster MariaDB en NVMe decente, pero la latencia de commit afectaba su API.
Alguien sugirió la clásica: relajar la durabilidad. Cambiaron innodb_flush_log_at_trx_commit y sync_binlog
para reducir la frecuencia de fsync. La latencia de escritura bajó. Todos celebraron. El cambio se declaró “seguro” porque “tenemos réplicas”.

Dos semanas después, un evento de energía afectó a un rack. No catastrófico—solo lo suficiente para reiniciar varios nodos.
Un primario volvió con transacciones faltantes que ya se habían reconocido a clientes. Las réplicas no las guardaron porque las transacciones perdidas
nunca llegaron a medio estable en el primario, y la automatización de failover promovió al nodo equivocado.
Ahora tenían un incidente de integridad de datos. Esos son los que hacen que la gente hable en voz baja en las reuniones.

El retroceso no fue “los ajustes de durabilidad son malvados”. Fue la falta de alineación entre requisitos de negocio y decisiones de ingeniería.
Si debes relajar la durabilidad, lo haces deliberadamente: con dispositivos con protección contra pérdida de energía, procedimientos claros de recuperación,
y un acuerdo explícito sobre qué significa “escritura reconocida”.

Revirtieron los ajustes, luego arreglaron el cuello de botella real: contención de log y presión de checkpoints.
Redimensionaron redo logs, confirmaron el enfriamiento para evitar throttling, y separaron los binlogs en un volumen distinto.
La latencia de la API mejoró otra vez—sin mentir a los usuarios sobre durabilidad.

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

Un equipo de servicios financieros ejecutaba MySQL en NVMe, pero trataban el almacenamiento como un componente de fiabilidad, no como una etiqueta de rendimiento.
Tenían una rutina: chequeos semanales de salud SMART, líneas base de temperatura bajo pico, y un pequeño conjunto de “comandos dorados” capturados en runbooks.
También monitorizaban p99 de fsync como métrica de primera clase.

Un martes, notaron un aumento sutil en la latencia de escritura—nada dramático, solo lo suficiente para empujar p95 de la API hacia arriba.
Empezaron las teorías habituales de “es la red”. El ingeniero on-call no discutió; sacó el runbook y ejecutó las comprobaciones.
SMART mostró temperaturas en ascenso, e iostat mostró w_await subiendo bajo carga.

Encontraron un ventilador fallido en el chasis. La unidad NVMe había empezado a throttlear en picos.
Reemplazaron el ventilador, restauraron el flujo de aire, la latencia volvió a la normalidad. Sin outage, sin mantenimiento de emergencia, sin resumen ejecutivo lleno de explicaciones vagas.
La solución fue aburrida. El resultado fue hermoso.

Chiste #2: La mejor optimización de rendimiento a veces es un ventilador de 20$—porque a los electrones tampoco les gusta ser asados.

Listas de verificación / plan paso a paso

Paso a paso: aislar “el almacenamiento es lento” de “la base de datos es lenta”

  1. Verifica la ubicación: confirma que datadir y tmpdir estén en el dispositivo previsto (Task 1–2).
  2. Confirma la presión del sistema: mpstat + iostat durante una ventana lenta (Task 8–9).
  3. Revisa esperas de BD: top waits de Performance Schema y estado de InnoDB (Task 10–11).
  4. Clasifica la carga:

    • Si predominan esperas de log: ruta de commit/fsync.
    • Si predominan lecturas de archivo de datos: problema de memoria/plan de consultas.
    • Si predominan mutex/bloqueos: problema de contención/diseño de transacciones.
  5. Valida la salud del hardware: advertencias SMART y temperatura (Task 5–6).
  6. Haz un cambio a la vez, con captura antes/después y plan de rollback.

Lista: preparación NVMe para MySQL/MariaDB

  • La unidad tiene protección contra pérdida de energía si dependes de caching de escritura seguro para latencia.
  • Las térmicas están monitorizadas; se valida el flujo de aire del chasis bajo pico.
  • La elección del sistema de archivos y opciones de montaje está estandarizada en la flota.
  • Las configuraciones de redo/binlog coinciden con los requisitos de durabilidad del negocio (no solo con benchmarks).
  • Existe margen de capacidad (no operes al 90%+ a menos que disfrutes sorpresas).
  • Puedes responder: “¿Evento de espera principal en pico?” y “¿p99 de fsync?”

Lista: puntos de decisión MySQL vs MariaDB (edición pragmática)

  • Compatibilidad: confirma la paridad de funciones de la que dependes (modo de replicación, GTIDs, herramientas).
  • Observabilidad: elige el que tu equipo pueda depurar rápido con habilidades y dashboards existentes.
  • Disciplina de actualizaciones: escoge el ecosistema donde puedas parchear regularmente con comportamiento predecible.
  • Herramientas operacionales: backups, verificación, failover, migraciones de esquema—esto importa más que microbenchmarks.

Preguntas frecuentes

1) ¿Debería NVMe superar siempre a un SSD SATA para MySQL?

No siempre en formas que notarás. Si estás limitado por CPU, bloqueos o tu working set cabe en memoria, NVMe no cambiará mucho.
NVMe brilla bajo alta concurrencia con I/O aleatorio y cuando necesitas menor latencia en la cola bajo carga sostenida de escritura.

2) ¿Por qué %iowait se mantiene bajo incluso cuando las consultas son lentas?

Porque el proceso puede estar esperando bloqueos, scheduling de CPU, stalls de memoria o mutex internos de MySQL.
Además, iowait es una métrica de contabilidad de CPU, no una medida directa de latencia de disco. Usa iostat y los eventos de espera de la BD en su lugar.

3) ¿Es seguro innodb_flush_log_at_trx_commit=2?

Es un trade-off de durabilidad. Puede perder hasta aproximadamente un segundo de transacciones comprometidas durante un crash/pérdida de energía.
“Seguro” depende del contrato de negocio con la realidad. Decide explícitamente, y no lo escondas detrás de “tuning de rendimiento”.

4) Mis esperas de archivo de log dominan. ¿Cuál es la primera perilla para tocar?

Primero mide. Luego considera la capacidad del redo log (muy pequeño causa churn de checkpoints) y la frecuencia de fsync del binlog.
Si la durabilidad debe permanecer estricta (1/1), probablemente necesitarás dispositivos con mejor latencia, SSDs con PLP, o reducir la concurrencia de commits en capa de aplicación.

5) ¿Importa la elección del sistema de archivos (ext4 vs XFS) para rendimiento de BD?

Puede importar, principalmente a través del comportamiento de latencia bajo writeback y cómo maneja metadata y patrones de asignación.
La ventaja mayor es consistencia y capacidad de medición a través de hosts. Elige uno, estandariza opciones de montaje y establece una línea base de p99 fsync.

6) ¿Cuál es la forma más fácil de saber si estoy limitado por lecturas o escrituras?

Mira Innodb_buffer_pool_reads y iostat lecturas vs escrituras, luego contrasta con Performance Schema.
Altas lecturas de disco y esperas de archivos de datos implican presión de lectura. Altas esperas de log y flushes pendientes implican presión de escritura/commit.

7) ¿Cómo puede un “disco más rápido” empeorar la contención de bloqueos?

Al aumentar el throughput de algunas operaciones, puedes incrementar la concurrencia y la presión sobre puntos calientes.
Commits más rápidos pueden causar que más transacciones choquen en las mismas filas o índices, llevando la contención al frente de la línea.

8) ¿Los discos NVMe de consumo son adecuados para bases de datos en producción?

A veces, para cargas no críticas. Para sistemas sensibles a durabilidad, la falta de protección contra pérdida de energía y la latencia sostenida inconsistente
suelen ser motivos de rechazo. Incluso cuando son “rápidos”, sus modos de falla rara vez son amables.

9) MySQL vs MariaDB: ¿cuál es más rápido en NVMe?

Depende de la versión, la carga, la configuración y las características utilizadas. En operación real, “más rápido” importa menos que
“más fácil de depurar de forma predecible” y “se comporta bien ante fallos”. Haz benchmark con tu propia carga y compara perfiles de espera, no solo QPS.

10) ¿Qué métrica única debo alertar para detectar temprano el dolor relacionado con almacenamiento?

Si sólo puedes elegir una, alerta sobre latencia relacionada con commits o tiempo de espera de archivo de log (desde la instrumentación de BD) más la temperatura del dispositivo.
Esas dos capturan una cantidad sorprendente de problemas del mundo real.

Conclusión: próximos pasos prácticos

NVMe es una gran herramienta. No sustituye a entender el perfil de esperas de tu base de datos.
Si tu sistema MySQL o MariaDB aún se siente lento después de pasar de SSD SATA a NVMe, asume nada y mide todo lo que importa:
ruta de commit, presión de checkpoints, misses del buffer pool y salud del dispositivo.

  1. Ejecuta el guion de diagnóstico rápido durante una ventana lenta y captura las salidas.
  2. Clasifica el cuello de botella (presión de lectura vs presión de escritura/commit vs contención/CPU).
  3. Arregla la restricción de mayor apalancamiento primero: planes/locks, dimensionamiento de redo/checkpoints, o térmicas/salud del hardware.
  4. Haz un cambio a la vez. Ten rollback. Baselinea latencia p95/p99, no solo throughput.

Tu objetivo no es “NVMe en todas partes”. Tu objetivo es una base de datos que se comporte de forma predecible en el pico, falle honestamente y pueda ser depurada rápidamente por humanos cansados.

← Anterior
Debian 13 Split DNS para VPN y LAN: una configuración limpia que no fallará tras el reinicio
Siguiente →
Correo electrónico: las inclusiones de SPF son un desastre — cómo simplificar sin afectar la entrega de correo

Deja un comentario