El checkout debería ser aburrido. Haces clic en “Realizar pedido”, se procesa el pago, llega el recibo y todos se van. Cuando el checkout tarda 6–20 segundos (o caduca), rara vez es “WordPress siendo WordPress”. Suele ser la base de datos haciendo exactamente lo que le mandaste—solo que no lo que querías.
He visto equipos pasar semanas intercambiando MySQL por MariaDB (o viceversa), añadir CPU, cachear páginas que no pueden cachearse, incluso culpar a pasarelas de pago. Luego un único ajuste en InnoDB cambia todo y la latencia desaparece. La trampa: hay otra configuración que a la gente le encanta cambiar y que solo enmascara el problema—hasta que un día se convierte en expectativas corruptas y restauraciones nocturnas.
Cómo se ve realmente la latencia en el checkout en producción
La “latencia” del checkout en WooCommerce rara vez es una sola cosa. Es un conjunto de pequeños pasos síncronos que se convierten en un gran retraso en tiempo real:
- Recálculo de totales del carrito e impuestos.
- Inserción de la fila del pedido (y a veces reactualizaciones múltiples).
- Reducciones de stock e inserciones de items del pedido.
- Actualizaciones de meta de cliente/sesión.
- El plugin de pago escribe logs, añade metadatos y puede hacer llamadas remotas.
- Correos transaccionales encolados o enviados.
En la base de datos, eso se traduce en un breve estallido de escrituras que deben ser consistentes. Si tu BD no puede absorber ese estallido—porque el redo log es demasiado pequeño, la latencia de fsync es alta, los bloqueos se acumulan o el buffer pool está subdimensionado—verás:
- Gran TTFB en
/checkouto/?wc-ajax=checkout. - Picos en “Threads running”.
- Esperas de bloqueo en InnoDB.
- CPU no necesariamente al máximo (la BD espera I/O).
- Los pagos se autorizan pero la página de confirmación se queda colgada (clásico: la web espera el commit en BD, la pasarela ya cobró).
Marco operativo: el checkout es tu camino de escritura. Tu camino de escritura es tu detector de verdad. Las cachés pueden ocultar mucho; no pueden ocultar la latencia de commit para siempre.
Las dos configuraciones: una soluciona, la otra enmascara
La configuración que realmente arregla la latencia del checkout (la mayoría de las veces): capacidad del redo log
Cuando los estallidos de escritura del checkout golpean InnoDB, el redo log es el amortiguador. Si es demasiado pequeño, InnoDB se ve obligado a hacer checkpoints frecuentes, forzando el vaciado de páginas sucias agresivamente. Eso convierte tu patrón “escribir y seguir” en “escribir, esperar, vaciar, esperar, checkpoint, esperar”. El checkout se vuelve una cola.
Enfoque MySQL 8+ (preferido): ajustar innodb_redo_log_capacity.
Enfoque MariaDB y MySQL antiguos: ajustar innodb_log_file_size (y innodb_log_files_in_group si aplica), entendiendo las implicaciones operativas.
Por qué esta es la configuración “arregla”: reduce la presión de checkpoints forzados durante estallidos de escritura. No engaña a la durabilidad; simplemente permite que InnoDB agrupe el trabajo con inteligencia.
La configuración que la gente cambia y que mayormente enmascara el problema: el toggle de fsync/durabilidad
El placebo con impacto es:
innodb_flush_log_at_trx_commit(ajustado a2o0)- a veces emparejado con
sync_binlogen un valor bajo (como0)
Sí, puede hacer el checkout más rápido porque dejas de pagar el coste completo de fsync por transacción. Pero no es una solución de rendimiento; es una concesión de durabilidad. Ante un fallo o corte de energía puedes perder transacciones comprometidas (o replicar de forma inconsistente si también relajas la durabilidad del binlog). Eso no es “afinación”, es elegir qué tipo de incidente prefieres.
Casos donde es aceptable: entornos efímeros, tiendas no críticas o situaciones donde se ha diseñado explícitamente para ello (cache con respaldo de batería, alimentación fiable y estás cómodo con una pequeña ventana de pérdida).
Casos donde te morderá: cualquier cosa con dinero real, inventario real, contracargos o expectativas regulatorias. Que es la mayoría de las tiendas WooCommerce que pagan nóminas.
Broma #1: Poner innodb_flush_log_at_trx_commit=0 es como quitarte el cinturón de seguridad porque te arruga la camisa.
Cómo se ve “una arregla, otra enmascara” en los gráficos
Cuando la capacidad del redo log es demasiado pequeña, a menudo verás:
- Picos en escrituras de disco y esperas de fsync.
- La edad del checkpoint de InnoDB alcanzando un techo.
- La latencia del checkout correlacionando con el vaciado en segundo plano.
Cuando “enmascaras” con innodb_flush_log_at_trx_commit=2, verás mejorar la latencia p95—pero no has atendido la presión de vaciado. Solo la has retrasado y reducido la frecuencia de fsync. Bajo carga sostenida, los bloqueos vuelven con otra forma (o aparecen como lag de replicación, tiempo de recuperación y conversaciones desagradables).
MySQL vs MariaDB: dónde realmente importan las diferencias
Para WooCommerce, la pregunta no es “cuál es más rápido en general”. Es “cuál falla de manera más predecible con mi carga, mis plugins y mi almacenamiento”. MySQL y MariaDB comparten ascendencia pero se desviaron lo suficiente como para que “reemplazo directo” sea marketing, no ingeniería.
1) Registro redo y comportamiento de checkpoints: el punto caliente aburrido
MySQL 8 introdujo innodb_redo_log_capacity, que es operativamente más amigable que el viejo baile de “parar servidor, mover ib_logfile*, reiniciar”. MariaDB aún usa comúnmente innodb_log_file_size (según versión), y la vía operativa puede ser más manual.
En la práctica, esto significa que MySQL 8 facilita hacer lo correcto: dimensionar los redo logs para estallidos de escritura sin planear una ventana de mantenimiento que todos “olvidan” programar.
2) Manejo de hilos bajo concurrencia: el pool de hilos de MariaDB puede ayudar
MariaDB ofrece un pool de hilos en algunas compilaciones/ediciones que puede estabilizar el rendimiento cuando tienes muchas conexiones de corta duración. WooCommerce detrás de PHP-FPM puede crear un patrón de churn de conexiones si no usas conexiones persistentes (y a menudo no deberías).
MySQL ha mejorado su manejo de concurrencia con el tiempo, pero si tu problema es “demasiadas conexiones/hilos” en lugar de bloqueos de I/O, el pool de hilos de MariaDB puede ser una ganancia pragmática.
3) Optimizador de consultas y diferencias en performance schema: la herramienta afecta el resultado
Cuando diagnosticas la latencia del checkout, necesitas visibilidad creíble de esperas, bloqueos y consultas calientes. El performance schema y la ergonomía del sys schema de MySQL 8 son generalmente excelentes para este trabajo. La instrumentación de MariaDB difiere; puede que tengas que confiar más en information_schema, el estado del motor y logs lentos específicos.
4) Comportamiento y valores por defecto de replicación: no asumas paridad
Si ejecutas réplicas para lecturas o failover, las diferencias en implementaciones GTID, formatos de binlog y ajustes por defecto importan. Las configuraciones de durabilidad que “enmascaran” son especialmente peligrosas aquí: puedes tener un checkout que “funcionó” en el primario pero que nunca quedó seguro en el binlog antes de un fallo.
5) El factor decisivo real: tu almacenamiento y la realidad de fsync
Ambos motores pueden ser rápidos en un buen almacenamiento y miserables en un almacenamiento malo. NVMe con política de caché de escritura sensata es otro universo respecto a discos en red con latencia impredecible. La latencia del checkout suele ser la historia de fsync en un disco triste.
Hechos e historia que explican las rarezas actuales
- Hecho 1: MariaDB se bifurcó de MySQL tras la adquisición de Sun por Oracle, y la compatibilidad ha sido “en su mayoría” más que “siempre”. Ese “en su mayoría” es donde viven los incidentes.
- Hecho 2: InnoDB no fue originalmente “el motor de MySQL”. Se convirtió en el predeterminado porque sobrevive cargas reales: recuperación tras fallo, bloqueo a nivel de fila y transacciones.
- Hecho 3: El viejo modelo de dimensionado del redo log (tamaño de archivo × archivos en el grupo) es la razón por la que cambiarlo históricamente requería borrar/renombrar ib_logfiles y reiniciar. MySQL 8 mejoró esto con gestión de capacidad del redo log.
- Hecho 4:
innodb_flush_log_at_trx_commitpor defecto es1por una razón: es la configuración duradera. La presión por rendimiento no cambió la física; solo cambió cuánta gente se siente tentada a hacer trampa. - Hecho 5: Las tablas núcleo de WooCommerce han crecido con el tiempo (order items, tablas meta, tablas de analíticas). El patrón de meta-tabla es flexible pero castiga lecturas sin índices y escrituras intensas.
- Hecho 6: Históricamente, WordPress por defecto usaba MyISAM en el pasado antiguo. Muchos mitos de “WordPress lento” vienen de esa era; los cuellos de botella modernos son distintos y más sutiles.
- Hecho 7: MariaDB introdujo Aria como reemplazo de MyISAM, pero WooCommerce no debería mezclar motores para escrituras transaccionales de pedidos a menos que te gusten los bugs misteriosos.
- Hecho 8: El “query cache” solía ser una característica de MySQL en la que la gente confiaba. Desapareció en MySQL 8 porque causaba contención. Si alguien te dice “habilita query cache”, estás escuchando una historia de fantasmas.
Guía rápida de diagnóstico (primero/segundo/tercero)
Así encuentras el cuello de botella sin pasar tres días discutiendo con un proveedor de plugins.
Primero: confirma que es espera en BD, no PHP o red
- Mide TTFB en el balanceador de carga / reverse proxy y compáralo con el tiempo de respuesta upstream.
- Revisa la saturación de PHP-FPM (procesos activos, slowlog).
- Si el upstream es lento y PHP no está al máximo, sospecha esperas de BD.
Segundo: identifica si estás limitado por I/O, bloqueos o CPU
- I/O-bound: alta latencia de fsync, vaciado de páginas sucias, esperas de InnoDB.
- Lock-bound: tiempos de espera por bloqueo, muchos threads esperando, filas/tablas calientes (frecuentemente
wp_optionso meta de pedidos). - CPU-bound: CPU alta, consultas pesadas, índices pobres, grandes ordenaciones/tablas temporales.
Tercero: arregla lo que elimina la cola
- Si es I/O-bound y el redo log es pequeño: aumenta la capacidad del redo log (la solución real).
- Si es lock-bound: reduce la contención (índices, alcance de la transacción, comportamiento de plugins).
- Si es CPU-bound: afina consultas, índices y buffer pool; detén la consulta que hace un table scan en tiempo de checkout.
Luego—y solo entonces—considera concesiones de durabilidad o cambiar de motor.
Tareas prácticas: comandos, salidas, decisiones (12+)
Todo lo siguiente está diseñado para ser ejecutado en un host Linux típico con MySQL/MariaDB. Reemplaza socket/credenciales según necesites. Cada tarea incluye: comando, salida de ejemplo, qué significa y qué decisión tomar.
Task 1: Confirmar versión y proveedor del servidor (no adivines)
cr0x@server:~$ mysql --version
mysql Ver 8.0.36 for Linux on x86_64 (MySQL Community Server - GPL)
Significado: Estás en MySQL 8.0. La afinación del redo log usa innodb_redo_log_capacity.
Decisión: No sigas consejos específicos de MariaDB (configuraciones de pool de hilos, tablas de estado diferentes) sin comprobar equivalentes.
Task 2: Verificar las configuraciones de durabilidad activas (detecta el “enmascaramiento”)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('innodb_flush_log_at_trx_commit','sync_binlog');"
innodb_flush_log_at_trx_commit 1
sync_binlog 1
Significado: Valores por defecto totalmente durables. Bueno para dinero real. También significa que los costes de fsync son reales y deben ser manejados con I/O adecuado y dimensionado de redo.
Decisión: Mantén esto en 1 salvo que tengas un modelo de riesgo escrito y probado para fallos.
Task 3: Medir presión de conexiones y concurrencia
cr0x@server:~$ mysql -Nse "SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Threads_connected';"
Threads_running 18
Threads_connected 120
Significado: 18 threads ejecutándose no es disparatado, pero 120 conectados sugiere churn de conexiones o alta concurrencia web.
Decisión: Si Threads_running se dispara durante estallidos de checkout, correlaciona con esperas por bloqueo e I/O a continuación.
Task 4: Comprobar tamaño del buffer pool de InnoDB (¿estás leyendo desde RAM?)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
innodb_buffer_pool_size 4294967296
Significado: Buffer pool de 4 GiB. En una tienda con tráfico sustancial y tablas meta grandes, esto puede ser insuficiente.
Decisión: Si el host tiene RAM disponible, apunta al 60–75% de memoria del sistema para hosts dedicados a BD (menos en hosts compartidos).
Task 5: Comprobar capacidad/tamaño del redo log (la palanca de “arreglo”)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'innodb_redo_log_capacity';"
innodb_redo_log_capacity 104857600
Significado: Capacidad de redo de 100 MiB. Para cargas transaccionales con estallidos, eso suele ser ridículamente pequeño.
Decisión: Aumentar a algo sensato (a menudo 1–4 GiB dependiendo de la tasa de escritura y tolerancia de recuperación). Valida espacio en disco y expectativas de tiempo de recuperación.
Task 6: Observar presión de checkpoint y flushing
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2025-12-29 09:17:21 0x7f3b3c0 INNODB MONITOR OUTPUT
=====================================
...
Log sequence number 112233445566
Log flushed up to 112233440000
Pages flushed up to 112233000000
Last checkpoint at 112232900000
...
Modified db pages 84210
...
Pending flushes (fsync) log: 0; buffer pool: 37
Significado: Muchas páginas modificadas y flushes pendientes del buffer pool indican que el flusher en segundo plano está ocupado. Si esto se dispara durante el checkout, estás limitado por I/O/redo.
Decisión: Aumenta la capacidad del redo y asegúrate de que tu almacenamiento puede manejar escrituras sostenidas; considera ajustar innodb_io_capacity solo tras medir el disco.
Task 7: Buscar esperas de bloqueo y deadlocks durante picos de checkout
cr0x@server:~$ mysql -Nse "SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_time'; SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_waits';"
Innodb_row_lock_time 98342
Innodb_row_lock_waits 217
Significado: Existen esperas por bloqueo. No necesariamente catastrófico, pero si se disparan durante el checkout, tienes contención.
Decisión: Identifica tablas/consultas calientes (tareas siguientes). Si las esperas de bloqueo correlacionan con actualizaciones autoload de wp_options o actualizaciones de stock, corrige índices/alcance de transacciones.
Task 8: Encontrar las consultas más lentas (usa el slow log, no sensaciones)
cr0x@server:~$ mysql -Nse "SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';"
slow_query_log ON
long_query_time 1.000000
Significado: El slow query log está activado, umbral 1s. Bien—los problemas del checkout a menudo esconden unas pocas consultas de 2–5s.
Decisión: Si está apagado, actívalo temporalmente y captura una ventana de checkout. No trabajes a ciegas.
Task 9: Inspeccionar latencia del disco y confirmar la realidad de fsync
cr0x@server:~$ iostat -x 1 3
Linux 6.5.0 (server) 12/29/2025 _x86_64_ (8 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.50 0.00 6.25 18.75 0.00 62.50
Device r/s w/s rkB/s wkB/s await svctm %util
nvme0n1 45.0 220.0 2200.0 9800.0 9.80 0.35 85.00
Significado: await ~10ms con alta utilización e iowait ~19%. Para una BD transaccional ocupada, 10ms promedio ya puede ser doloroso. Si se dispara más durante picos de checkout, lo notarás.
Decisión: Si await es alto: arregla el almacenamiento (NVMe, política de caché del controlador RAID, sistema de ficheros, vecino ruidoso) antes de culpar al motor SQL.
Task 10: Buscar bloat de tablas y faltas de índices en puntos calientes de WooCommerce
cr0x@server:~$ mysql -e "SELECT table_name, engine, table_rows, round((data_length+index_length)/1024/1024,1) AS MB FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name IN ('wp_postmeta','wp_options','wp_woocommerce_order_items','wp_woocommerce_order_itemmeta') ORDER BY MB DESC;"
+------------------------------+--------+------------+--------+
| table_name | engine | table_rows | MB |
+------------------------------+--------+------------+--------+
| wp_postmeta | InnoDB | 8200000 | 2150.4 |
| wp_options | InnoDB | 98000 | 210.7 |
| wp_woocommerce_order_itemmeta| InnoDB | 2400000 | 640.9 |
| wp_woocommerce_order_items | InnoDB | 310000 | 96.2 |
+------------------------------+--------+------------+--------+
Significado: Tablas meta grandes. Eso es normal. La pregunta es si las consultas que las tocan están indexadas y si wp_options tiene bloat de autoload.
Decisión: Planifica revisión de índices y limpieza de opciones. Las tablas grandes no son un crimen; el acceso sin índices sí.
Task 11: Detectar bloat de autoload (un asesino silencioso del checkout)
cr0x@server:~$ mysql -e "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS autoload_mb FROM wp_options WHERE autoload='yes';"
+-------------+
| autoload_mb |
+-------------+
| 18.74 |
+-------------+
Significado: ~19 MiB autoload en cada petición. Eso es pesado; incrementa churn de memoria y tiempo de consulta, y puede amplificar la contención si las opciones se actualizan con frecuencia.
Decisión: Reduce autoload. Identifica culpables; mueve caches transitorios fuera de autoload; audita plugins que vuelcan arrays gigantes en options.
Task 12: Encontrar qué opciones son las culpables
cr0x@server:~$ mysql -e "SELECT option_name, ROUND(LENGTH(option_value)/1024,1) AS kb FROM wp_options WHERE autoload='yes' ORDER BY LENGTH(option_value) DESC LIMIT 10;"
+---------------------------+--------+
| option_name | kb |
+---------------------------+--------+
| plugin_x_cache_blob | 2048.0 |
| woocommerce_sessions | 1536.0 |
| theme_builder_settings | 980.4 |
| plugin_y_rules | 620.7 |
| rewrite_rules | 410.2 |
| cron | 350.1 |
| widget_text | 290.5 |
| wp_user_roles | 210.3 |
| plugin_z_feed_state | 185.9 |
| woocommerce_tax_rates | 160.4 |
+---------------------------+--------+
Significado: Algunos plugins almacenan megabytes en autoload. Eso no es “WordPress”; es usar la tabla options como contenedor indiscriminado.
Decisión: Cambia autoload a ‘no’ donde sea seguro, o ajusta configuraciones de plugins. Valida el comportamiento en staging.
Task 13: Verificar formato de binlog y durabilidad para seguridad transaccional
cr0x@server:~$ mysql -Nse "SHOW VARIABLES WHERE Variable_name IN ('log_bin','binlog_format','sync_binlog');"
log_bin ON
binlog_format ROW
sync_binlog 1
Significado: Amigable para replicación y durable. Binlog en modo row reduce sorpresas en replicación para escrituras complejas.
Decisión: Si alguien sugiere sync_binlog=0 para “arreglar checkout”, pregunta cuál es su plan de restauración cuando el binlog no contenga pedidos pagados.
Task 14: Identificar eventos de espera principales (performance schema de MySQL 8)
cr0x@server:~$ mysql -e "SELECT event_name, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1000000000000,2) AS seconds_waited FROM performance_schema.events_waits_summary_global_by_event_name WHERE event_name LIKE 'wait/io/file/innodb/%' ORDER BY SUM_TIMER_WAIT DESC LIMIT 8;"
+------------------------------------------+------------+----------------+
| event_name | COUNT_STAR | seconds_waited |
+------------------------------------------+------------+----------------+
| wait/io/file/innodb/innodb_redo | 18422091 | 812.34|
| wait/io/file/innodb/innodb_data_file | 3922011 | 244.12|
| wait/io/file/innodb/innodb_log_file | 1200099 | 120.44|
| wait/io/file/innodb/innodb_temp_file | 22011 | 11.02|
+------------------------------------------+------------+----------------+
Significado: Las esperas de redo dominan. Eso es una flecha roja hacia el dimensionado del redo y la latencia del disco.
Decisión: Aumenta la capacidad del redo y vuelve a comprobar. Si las esperas de redo persisten, el siguiente sospechoso es el almacenamiento/fsync.
Task 15: Capturar una vista en tiempo real durante una prueba de checkout
cr0x@server:~$ mysqladmin -i 1 -c 5 processlist status
Uptime: 123456 Threads: 125 Questions: 9933221 Slow queries: 18 Opens: 322 Flush tables: 1 Open tables: 256 Queries per second avg: 80.50
Id: 31142 User: wp Host: 10.0.2.15:43218 db: shop Command: Query Time: 3 State: Waiting for handler commit Info: UPDATE wp_postmeta SET meta_value='...'
Id: 31151 User: wp Host: 10.0.2.15:43222 db: shop Command: Query Time: 3 State: Waiting for handler commit Info: INSERT INTO wp_posts ...
Uptime: 123457 Threads: 128 Questions: 9933310 Slow queries: 18 Opens: 322 Flush tables: 1 Open tables: 256 Queries per second avg: 92.10
Significado: “Waiting for handler commit” es un signo clásico de presión en commit/fsync (no siempre, pero a menudo). Si las solicitudes de checkout se quedan colgadas aquí, estás pagando la latencia del almacenamiento en tiempo de commit.
Decisión: No tires cachés a esto. Arregla el camino de escritura: capacidad del redo, latencia del almacenamiento y comportamiento de flush.
Tres microhistorias corporativas desde las trincheras del checkout
Microhistoria 1: El incidente causado por una suposición errónea
Una empresa minorista mediana migró una pila WooCommerce de MySQL a MariaDB porque “es un reemplazo drop-in y más rápido”. El plan de migración era limpio en papel: dump, import, cambiar endpoints, listo. Hicieron una prueba de navegación rápida, procesaron un par de pedidos en staging y lo pusieron en producción un martes tranquilo.
El checkout se vio bien por un día. Luego salió un email de marketing y el sitio alcanzó una forma de tráfico familiar: mucha navegación, seguida de un estallido agudo de checkouts en 10–15 minutos. Los pedidos empezaron a hacer timeouts, pero las autorizaciones de pago seguían ocurriendo. Llegaron tickets de soporte: “Me cobraron pero no recibí confirmación.” Ya sabes cómo sigue.
La suposición errónea no fue “MariaDB es mala”. Fue “el cambio de motor arreglará la latencia”. El problema real era la latencia de almacenamiento bajo fsync más redo logs demasiado pequeños. La nueva BD se comportó de forma ligeramente distinta y los bloqueos parecían nuevos, pero la física era la misma. Simplemente estaban haciendo checkpoints constantemente bajo presión.
Se estabilizaron aumentando el dimensionado del redo log adecuadamente y ajustando la capacidad de I/O al dispositivo. Luego cerraron la brecha operativa: pruebas de carga que incluyeran estallidos de checkout, no solo visitas a la home. Nadie disfrutó la semana, pero la tienda dejó de cobrar a clientes en el vacío.
Microhistoria 2: La optimización que salió mal
Una agencia SaaS que corría múltiples tiendas WooCommerce por nodo tenía una queja recurrente: “el checkout es lento en picos”. Alguien vio que innodb_flush_log_at_trx_commit estaba en 1 y decidió que era “demasiado estricto”. Lo cambiaron a 2 en toda la flota durante una ventana de mantenimiento. La latencia mejoró de inmediato. Todos aplaudieron.
Dos meses después, un host se reinició bruscamente por un kernel panic. Una tienda reportó pedidos faltantes: los clientes tenían recibos del proveedor de pagos, pero los pedidos no estaban en WooCommerce. El equipo inicialmente culpó al plugin de pagos. Luego encontraron la brecha: transacciones que habían sido reconocidas a nivel de aplicación no tenían garantizado que su redo hubiera sido vaciado en el momento del crash. Algunas escrituras simplemente nunca llegaron a almacenamiento estable.
Habían cambiado rendimiento por una pequeña pero real ventana de pérdida de datos. No siempre es catastrófico. Es catastrófico exactamente cuando importa: cuando el servidor muere en el milisegundo equivocado. Que es lo que los servidores hacen, eventualmente, con un talento impresionante para el timing.
La solución no fue “nunca afinar durabilidad”. Fue dejar de usar la relajación de durabilidad como muleta de rendimiento. Revirtieron a 1, aumentaron la capacidad del redo log y mudaron las peores tiendas a mejor almacenamiento. El checkout siguió siendo suficientemente rápido y los “pedidos pagados faltantes” dejaron de existir.
Microhistoria 3: La práctica aburrida pero correcta que salvó el día
Un equipo empresarial ejecutaba WooCommerce como un “negocio secundario” en una plataforma compartida con otras apps. Nada emocionante, que es exactamente lo que quieres. Su SRE insistía en dos prácticas aburridas: mantener el slow query log habilitado (con rotación sensata) y hacer una revisión semanal de “top 10 queries”. Nada heroico, solo higiene.
Un viernes, la latencia del checkout subió. No una caída dura—solo el tipo de lentitud que mata la conversión mientras nadie recibe una alerta. Porque el slow log ya estaba allí, no tuvieron que “activar el logging durante el incidente”, que es la versión operativa de aprender a nadar en medio de una inundación.
El slow log mostró un nuevo patrón de consultas que martillaba wp_options con un full scan. Una actualización de plugin había introducido una consulta que ignoraba un acceso amigable a índices y se ejecutaba en tiempo de checkout. Revirtieron el plugin, luego añadieron un índice dirigido donde era apropiado y abrieron un ticket al proveedor con evidencia.
El checkout se recuperó en una hora. Sin cambio de motor, sin trampas de durabilidad, sin añadir servidores. Solo la competencia aburrida de tener datos cuando los necesitas.
Errores comunes: síntoma → causa raíz → arreglo
1) Síntoma: p95 del checkout salta durante picos de tráfico; la CPU está baja
Causa raíz: Checkpointing forzado de InnoDB por redo logs minúsculos; la presión de flush en disco crea stalls.
Arreglo: Aumentar innodb_redo_log_capacity (MySQL 8) o innodb_log_file_size (MariaDB/MySQL antiguos). Valida la latencia de almacenamiento con iostat.
2) Síntoma: “Waiting for handler commit” domina el processlist
Causa raíz: Latencia de commit/fsync (almacenamiento malo, dispositivo saturado o demasiados commits pequeños).
Arreglo: Mejora el almacenamiento, mantén durabilidad en 1, aumenta la capacidad del redo; reduce número de transacciones donde sea posible (comportamiento de plugins).
3) Síntoma: timeouts por espera de bloqueo alrededor del checkout
Causa raíz: Filas/tablas calientes (filas de stock, filas de options, tablas de sesión), transacciones largas, índices faltantes.
Arreglo: Identifica las consultas que bloquean; añade índices; reduce el alcance de la transacción; corrige plugins que mantienen transacciones abiertas mientras hacen llamadas remotas.
4) Síntoma: cambiar MySQL ↔ MariaDB “ayuda” pero no lo soluciona
Causa raíz: El cambio de motor modificó el comportamiento por defecto pero no arregló los problemas de I/O y esquema subyacentes.
Arreglo: Trata el motor como una elección, no como una cura. Arregla el dimensionado del redo, buffer pool, almacenamiento y puntos calientes primero.
5) Síntoma: checkout lento solo después de instalar un plugin “de rendimiento”
Causa raíz: El plugin añade logging síncrono, opciones autoload excesivas o ejecuta consultas pesadas en hooks de checkout.
Arreglo: Desactiva el plugin para confirmar; mueve el logging a asíncrono; limpia el autoload bloat; perfila consultas durante checkout.
6) Síntoma: lag de réplicas cuando hay picos de checkouts
Causa raíz: El primario está limitado por I/O; ajustes de flush de binlog; la réplica aplica transacciones grandes lentamente.
Arreglo: Arregla primero el camino de escritura del primario; verifica sync_binlog=1; asegúrate de que el almacenamiento de la réplica sea comparable; monitoriza el apply lag.
Listas de verificación / plan paso a paso
Paso a paso: arreglar la latencia del checkout sin apostar tus datos
- Latencia base: captura p50/p95 para el endpoint de checkout e indicadores de tiempo de commit en BD.
- Confirma motor/versión: MySQL 8 vs MariaDB cambia los controles y los dashboards.
- Mide latencia de almacenamiento: ejecuta
iostat -xdurante un estallido controlado de checkouts. - Inspecciona dimensionado de redo: si el redo es <1 GiB en una tienda real, asume que está subdimensionado hasta demostrar lo contrario.
- Aumenta la capacidad de redo con seguridad: MySQL 8 puede hacerlo más fácilmente; planifica mantenimiento para MariaDB/versiones antiguas.
- Mantén la durabilidad sensata: deja
innodb_flush_log_at_trx_commit=1salvo que tengas un modelo de riesgo escrito y probado. - Revisa buffer pool: asegúrate de que el working set caliente quepa; evita swapping.
- Encuentra puntos calientes de bloqueo: usa el estado de InnoDB, esperas por bloqueo y processlist durante checkout.
- Limpia bloat de autoload: reduce MB en autoload; es fruta fácil con gran impacto.
- Indexa lo que toca el checkout: especialmente postmeta y patrones de acceso de order meta usados por plugins.
- Valida bajo carga de estallido: no pruebes con un pedido; prueba con 20–50 checkouts en una ventana corta.
- Despliegue cuidado: una tienda/nodo a la vez, observa métricas y luego expande.
Cuándo elegir MySQL vs MariaDB para WooCommerce
- Elige MySQL 8 si quieres instrumentación integrada fuerte, gestión moderna del redo y amplia compatibilidad con proveedores gestionados.
- Elige MariaDB si tu plataforma lo estandariza, te beneficias de su pool de hilos/características de concurrencia y tu equipo operativo conoce bien sus peculiaridades.
- Evita cambiar como “solución de rendimiento” a menos que puedas nombrar el mecanismo exacto que esperas mejorar (y cómo lo medirás).
Broma #2: “Vamos a cambiar de base de datos” es la versión adulta de apagar y encender el monitor porque la hoja de cálculo se ve mal.
Una cita que deberías tatuarte en el runbook (mentalmente)
Idea parafraseada de John Allspaw: la confiabilidad es una característica, y no se obtiene por accidente.
Preguntas frecuentes
1) ¿MariaDB es más rápido que MySQL para WooCommerce?
A veces, en escenarios de concurrencia específicos. Pero la latencia del checkout suele ser más a menudo presión de I/O y redo/checkpoint que velocidad bruta de consultas. Elige según encaje operativo y observabilidad, no por folklore.
2) ¿Cuál es la única solución más común para la latencia del checkout?
Aumentar la capacidad/tamaño del redo log para que InnoDB deje de hacer checkpoints hasta morir durante estallidos de escritura. Luego confirma que la latencia del almacenamiento es razonable.
3) ¿Por qué no simplemente poner innodb_flush_log_at_trx_commit=2?
Porque es una concesión de durabilidad. Puede perder transacciones recientemente comprometidas ante un fallo. No es teórico; es exactamente lo que significa la configuración.
4) ¿Qué tamaño deben tener los redo logs?
No hay un número único. Para muchas tiendas WooCommerce con tráfico real, 1–4 GiB es un rango inicial común. Balanceas: menos stalls vs recuperación de crash más larga. Mide antes y después.
5) Si mi CPU está baja, ¿por qué el checkout es lento?
Porque la BD puede estar esperando I/O (fsync, escrituras a archivos de datos) o bloqueos. Los gráficos de CPU no muestran bien las esperas; el performance schema y las métricas de I/O sí.
6) ¿Pueden las opciones autoload afectar realmente el checkout?
Sí. El bloat de autoload incrementa el trabajo por petición, churn de memoria y puede crear contención si las opciones se actualizan con frecuencia. Es un clásico “muerte por conveniencia”.
7) ¿Debería poner las tablas de WooCommerce en MyISAM/Aria por velocidad?
No. El checkout es transaccional. Quieres recuperación tras fallo y bloqueo a nivel de fila. Si cambias eso por un benchmark, en producción acumularás deuda con intereses.
8) ¿Necesito un query cache para acelerar WooCommerce?
No. MySQL 8 lo eliminó. Usa índices adecuados, dimensionado del buffer pool y caching a nivel de aplicación donde tenga sentido (no en checkout).
9) ¿Qué pasa si aumentar la capacidad del redo no ayuda?
Entonces tu cuello de botella probablemente sea la latencia del almacenamiento, contención de bloqueos o una ruta de consulta específica lenta durante el checkout (a menudo causada por plugins). Usa resúmenes de eventos de espera y logs lentos para identificarlo.
10) ¿Debería moverme a un servicio de base de datos gestionado?
Si tu limitación actual es “no podemos operar almacenamiento y backups de forma fiable”, sí—los servicios gestionados pueden eliminar muchos modos de fallo. Pero no arreglarán patrones de esquema malos o comportamiento de plugins por arte de magia.
Próximos pasos que puedes hacer hoy
- Ejecuta las comprobaciones de versión y durabilidad. Confirma que no estás ya “enmascarando” el problema.
- Mide la latencia del disco durante un pequeño estallido de checkouts de prueba. Si
awaites feo, arregla primero el almacenamiento. - Comprueba la capacidad/tamaño del redo. Si es minúsculo, auméntalo con cuidado y monitoriza la presión de checkpoints.
- Mide esperas por bloqueo e inspecciona el bloat de autoload en
wp_options. Limpia los principales culpables. - Habilita/verifica el logging de consultas lentas y captura una ventana de checkout. Arregla lo que está realmente lento, no lo que está de moda.
Si haces solo una cosa: deja de tratar las configuraciones de durabilidad como afinación de rendimiento. Dimensiona los redo logs, verifica el disco y vuelve a hacer el checkout aburrido.