Alquilas un VPS modesto. Dos vCPU, unos gigas de RAM, almacenamiento “SSD” que resulta ser “el vecino ruidoso de otra persona”, y una carga de producción que
no es sofisticada: solo usuarios, pedidos, sesiones y algunos trabajos en segundo plano. Entonces alguien dice: “Usemos MongoDB, es NoSQL, escala”. Lo despliegas.
Funciona. Durante una semana. Luego la latencia se dispara, la media de carga sube y tus gráficos parecen un sismógrafo durante un apocalipsis menor.
Este modo de fallo es aburridamente común: una elección de base de datos basada en sensaciones en lugar de física. La física son la RAM, el I/O de disco y cómo se comporta
tu motor cuando no cabe en memoria. En VPS pequeños, ese es todo el juego.
Qué realmente mata el rendimiento del VPS
“Rendimiento de base de datos” en un VPS normalmente no trata de planes de consulta exóticos o rendimiento teórico. Trata de lo que sucede cuando tu conjunto de trabajo no cabe en la RAM,
tu disco es más lento de lo que crees y tu configuración asume una máquina tipo servidor en lugar de una VM económica.
1) Las fallas de RAM se convierten en lecturas de disco, y las lecturas de disco en latencia
MySQL (InnoDB) y MongoDB (WiredTiger) dependen en gran medida del caché. Cuando tu caché es demasiado pequeño, realizan paginación de datos desde el almacenamiento constantemente. En un VPS compartido,
ese almacenamiento puede ser “SSD” con la profundidad de cola secuestrada por otro inquilino. Tu aplicación lo ve como parones aleatorios de 200–800 ms y comienza a reintentar. Los reintentos aumentan
la concurrencia. La concurrencia aumenta la presión sobre el disco. Ahora tienes un bucle de retroalimentación con la personalidad de una trituradora de madera.
2) Overcommit y el OOM killer no negocian
En máquinas pequeñas, “solo dale más caché a la base de datos” a menudo significa “deja que Linux la mate después”. MongoDB en particular puede parecer estable hasta que el conjunto de trabajo crece
ligeramente; entonces empieza a competir con la caché de página del SO y todo lo demás. Cuando el kernel decide que la memoria se acabó, no envía un correo educado. Elige un proceso y lo termina.
3) La amplificación de escritura es el asesino silencioso del presupuesto
Las bases de datos no escriben lo que crees que escriben. Escriben WAL/journal, escriben páginas sucias, compactan/mezclan, hacen fsync, actualizan metadatos.
La “actualización de un documento” que imaginaste puede ser múltiples pequeñas escrituras aleatorias más trabajo de mantenimiento en segundo plano. En almacenamiento de VPS, las escrituras aleatorias son caras,
y las escrituras aleatorias sostenidas son la forma de descubrir que tu “SSD” es en realidad “una tarjeta RAID de 2013 compartida con 40 desconocidos”.
4) El modelo de datos equivocado convierte CPU en calor y I/O en llanto
La consulta más cara es la que no indexaste porque asumiste “NoSQL significa sin esquema, sin planificación”. La segunda más cara es la que no puede indexarse eficazmente porque el modelo de datos fomenta documentos que cambian de forma,
anidamiento profundo o “simplemente guarda arrays y filtra en el código de la aplicación”. Aquí es donde MongoDB se usa mal: no porque MongoDB sea malo, sino porque es indulgente hasta que de repente deja de serlo.
Una cita que debería estar en cada rotación de on-call: “La esperanza no es una estrategia.”
— General Gordon R. Sullivan.
Guía rápida de diagnóstico
Cuando un VPS se derrite, no tienes tiempo para debates filosóficos sobre teoría relacional. Necesitas una secuencia de triage que encuentre el cuello de botella en minutos.
Aquí está el orden que gana la mayoría de los incidentes.
Primero: ¿es CPU, memoria o disco?
- Limitado por disco: iowait alto, fsync lento, profundidad de cola en aumento, picos de latencia que se correlacionan con ráfagas de escritura.
- Limitado por memoria: actividad de swap, fallos de página mayores, OOM kills, fallos de caché de la base de datos, caída repentina de rendimiento al crecer el conjunto de datos.
- Limitado por CPU: CPU de usuario alta, consultas lentas que consumen CPU, escaneos con expresiones regulares, parseo de JSON, sobrecarga por compresión o cifrado.
Segundo: ¿la base de datos es el cuello de botella o la aplicación?
- La saturación del pool de conexiones y la acumulación de hilos a menudo parecen “base de datos lenta” pero son “aplicación mal dimensionada”.
- La contención de bloqueos parece consultas lentas pero suele ser una fila/documento caliente que hace que todos los demás esperen.
Tercero: identifica las 1–3 consultas/operaciones que hacen daño
- En MySQL: log de consultas lentas +
EXPLAIN+ estado de InnoDB. - En MongoDB: profiler +
explain()+ currentOp.
Cuarto: verifica el comportamiento del caché
- InnoDB: tasa de aciertos del buffer pool, vaciado de páginas sucias, presión del redo log.
- WiredTiger: uso de caché, presión de eviction, comportamiento de checkpoints.
Chiste #1: Si “arreglas” la latencia reiniciando la base de datos cada mañana, has inventado un scheduler, no fiabilidad.
MySQL vs MongoDB: los verdaderos compromisos
MySQL: predecible, aburrido y brutalmente eficiente cuando se modela bien
MySQL con InnoDB es una bestia OLTP de propósito general. Le gustan los datos estructurados, patrones de consulta estables e índices bien elegidos. No está de moda.
Eso es una característica.
En un VPS pequeño, MySQL suele ganar porque:
- El caché de InnoDB es sencillo: dimensionas el buffer pool y normalmente puedes razonar sobre las tasas de aciertos.
- Optimizador de consultas + índices manejan muchos patrones de acceso sin obligarte a incrustar todo en un solo registro.
- Herramientas operativas maduras: logs lentos, performance_schema, dashboards comunes y flujos de backup/restore predecibles.
- La sobrecarga por tamaño de datos suele ser menor que en modelos documentales con mucho JSON, especialmente si normalizas campos repetitivos.
MongoDB: potente, flexible y fácil de malutilizar hasta convertirlo en una máquina que agota el disco
MongoDB brilla cuando tus datos tienen forma natural de documento, cuando necesitas evolución de esquema flexible, cuando la desnormalización simplifica lecturas y cuando aceptas
los compromisos operativos. Pero en VPS pequeños, a menudo se elige por la razón equivocada: “NoSQL escala”. Eso no es un plan; es un horóscopo.
MongoDB puede ser perfectamente rápido en un VPS si:
- Tu conjunto de trabajo cabe en RAM o tienes almacenamiento rápido y consistente.
- Diseñas índices como si tu trabajo dependiera de ellos (porque así es).
- Controlas el crecimiento de documentos y evitas actualizaciones patológicas.
- Entiendes la eviction de WiredTiger y el dimensionamiento de caché.
La realidad del VPS: las cajas pequeñas castigan la sobrecarga
Las bases de datos documentales a menudo se usan con documentos más grandes de lo necesario. Documentos más grandes significan que caben menos en caché. Menos en caché significa más lecturas desde disco.
En un VPS restringido, cada fallo de caché es un impuesto que pagas con intereses.
Mientras tanto, las “tablas aburridas” de MySQL tienden a ser compactas, indexadas y accesadas mediante planes de consulta predecibles. No es magia; es simplemente menos sobrecarga por unidad de datos útiles.
Transacciones, restricciones y corrección bajo presión
Para muchas aplicaciones de producción, el asesino de rendimiento oculto no es la velocidad, sino la lógica compensatoria que añades cuando la corrección es incierta. Las restricciones de MySQL,
las claves foráneas (cuando se usan con cuidado) y la semántica transaccional reducen la cantidad de trabajo de “verificar en código de aplicación”.
MongoDB tiene soporte de transacciones sólido en versiones modernas, pero las transacciones multi-documento añaden sobrecarga y pueden desplazar tu cuello de botella hacia bloqueos, presión del oplog o latencia de replicación.
Si tu app necesita frecuentemente invariantes entre entidades, tendrás que o bien incrustar agresivamente (lo que crea documentos calientes y grandes) o construir un sistema relacional sobre un almacén documental. Rara vez es la ruta más barata en un VPS.
Los costos reales de replicación y durabilidad
Si ejecutas MongoDB correctamente, ejecutas un replica set. Si ejecutas MySQL correctamente, ejecutas replicación (o al menos backups y binlogs). Ambos tienen sobrecarga.
Pero la “postura de corrección por defecto” de MongoDB a menudo arrastra a los equipos a ejecutar tres nodos incluso cuando solo presupuestaron uno. En un despliegue pequeño, eso no es solo costo: es complejidad operativa y más superficies de fallo.
Contexto histórico y datos interesantes
Un poco de historia ayuda a explicar por qué estos sistemas se comportan como lo hacen y por qué “NoSQL porque está de moda” es un error organizativo recurrente.
- MySQL se creó a mediados de los 90 y se convirtió en la base de datos por defecto de la web temprana porque era rápido, simple y fácil de desplegar.
- InnoDB se convirtió en el motor por defecto en MySQL 5.5 (2010), trayendo recuperación ante fallos y bloqueo a nivel de fila como experiencia estándar.
- MongoDB apareció en 2009, nacido en una era en la que los desarrolladores luchaban contra esquemas rígidos y escalar SQL horizontalmente todavía se consideraba “difícil”.
- El término “NoSQL” se popularizó alrededor de 2009 como bandera para alternativas a las bases de datos relacionales, no como garantía de rendimiento.
- El motor WiredTiger de MongoDB se convirtió en el predeterminado en MongoDB 3.2 (2016), reemplazando MMAPv1 y cambiando drásticamente el comportamiento de memoria y disco.
- El tipo JSON de MySQL llegó en MySQL 5.7 (2015), una admisión silenciosa de que los datos semi-estructurados son normales y los motores relacionales pueden manejarlos.
- Los almacenes de documentos a menudo cambian amplificación de escritura por conveniencia de lectura mediante desnormalización; en discos lentos, esas escrituras aparecen como latencia y presión de checkpoints.
- Los replica sets y el consenso (p. ej., elecciones) introducen estados operativos que los despliegues de nodo único nunca ven, como rollbacks y comportamiento para prevenir split-brain.
Tres micro-historias corporativas desde las trincheras
Micro-historia #1: El incidente causado por una suposición equivocada
Un equipo SaaS de tamaño medio lanzó una nueva funcionalidad: cronologías de actividad de usuario. Alguien afirmó que un almacén documental era “obviamente la opción correcta” porque las entradas de la cronología parecían eventos JSON. Eligieron MongoDB y almacenaron la cronología de cada usuario como un array dentro de un único documento: un documento por usuario, añadiendo nuevos eventos al array.
En staging fue precioso. Las lecturas eran rápidas: obtener un documento y renderizar la cronología. Las escrituras también eran “rápidas” porque el conjunto de datos era pequeño y todo vivía en memoria.
La especificación del VPS era modesta pero parecía suficiente.
En producción, algunos usuarios pesados crearon documentos enormes. Las actualizaciones se volvieron costosas porque cada append no era solo “añadir un elemento”: desencadenaba comportamiento de crecimiento del documento,
escrituras de página más frecuentes y churn de caché. De repente el conjunto de trabajo no cabía. El I/O de disco se disparó durante las horas pico y la base de datos empezó a expulsar caché constantemente.
La suposición equivocada fue simple: “Una cronología de usuario es un documento”. No lo era. Era una colección con límites naturales de paginación. La solución también fue simple:
almacenar eventos como documentos separados con un índice en (user_id, created_at), paginar las lecturas y limitar la retención. El postmortem no trató de que MongoDB fuera lento.
Trató de que lo usaron como un almacén de blobs conveniente y luego se sorprendieron cuando el blob creció.
Micro-historia #2: La optimización que salió mal
Otra compañía ejecutaba MySQL en un VPS y estaba cansada del uso de disco. Un ingeniero bien intencionado decidió comprimir más: formatos de fila más grandes, campos más compactos y uso agresivo de JSON para “evitar joins”. Parecía una victoria: los datos se encogieron, las copias de seguridad fueron más pequeñas y los dashboards mostraron reducción en el crecimiento del almacenamiento.
Entonces la utilización de CPU subió, la latencia p95 se duplicó y la aplicación empezó a agotar timeouts durante picos de tráfico. El equipo inicialmente culpó a la red.
No era la red.
La “optimización” salió mal porque trasladó trabajo desde el disco a la CPU en el peor momento. La extracción de JSON y las consultas con muchas funciones impidieron el uso de índices.
La compresión redujo el almacenamiento pero aumentó el costo de CPU y volvió las lecturas menos amigables para la caché. Su tasa de aciertos del buffer pool parecía bien, pero las consultas aún consumían ciclos.
Revirtieron el diseño con mucho JSON, restauraron columnas relacionales adecuadas para las rutas calientes y usaron la compresión solo donde no bloqueaba el indexado.
La lección no fue “nunca comprimas”. La lección fue: no optimices el almacenamiento sin medir CPU y planes de consulta. En un VPS pequeño no tienes CPU extra para malgastar.
Micro-historia #3: La práctica aburrida pero correcta que salvó el día
Un equipo relacionado con pagos ejecutaba MongoDB para un event store interno y MySQL para facturación. No tenían gran presupuesto de operaciones, así que hicieron una cosa poco sexy de forma consistente:
probaban restauraciones mensualmente. No “tenemos backups”. Restauraciones reales, en una máquina limpia, con una lista de verificación y un reloj.
Una noche su proveedor de VPS tuvo un problema de almacenamiento a nivel de host. La VM se reinició en un sistema de archivos montado, pero los archivos de la base de datos estaban inconsistentes. La base de datos
no arrancó limpiamente. El pánico duró unos diez minutos, lo cual es un récord personal.
Restauraron el snapshot más reciente, reprodujeron logs donde aplicaba y volvieron a estar dentro de su RTO. El análisis post-incidente mostró que su monitorización era decente,
pero su ventaja real fue la confianza: ya sabían que la cadena de backups funcionaba y cuánto tardaba.
La práctica aburrida salvó el día porque los desastres nunca son divertidos. Son del tipo en que te das cuenta de que no tienes idea de si tus backups son reales.
Tareas prácticas: comandos, salidas, decisiones
Estas son las comprobaciones que realmente ejecuto en servidores de producción pequeños. Cada tarea incluye: un comando, qué significa una salida típica y qué decisión tomar a continuación.
Ejecútalas durante un incidente, o mejor: ejecútalas ahora mientras el sistema está tranquilo y registra líneas base.
Tarea 1: Confirma si estás limitado por CPU o I/O
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 41200 98200 981200 0 0 12 34 180 420 12 4 80 4 0
4 2 0 19800 62100 712300 0 0 220 540 260 880 15 6 44 35 0
3 1 0 20500 62100 710900 0 0 180 610 250 840 14 6 48 32 0
Significado: El wa (iowait) saltó a ~35%. Esa es una firma clásica de “el disco es el cuello de botella”, especialmente si las quejas de latencia coinciden.
Decisión: Deja de adivinar sobre consultas primero; verifica la latencia del disco y la presión del caché de la base de datos a continuación.
Tarea 2: Comprueba la latencia real del disco y la cola
cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server) 12/30/2025 _x86_64_ (2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
14.22 0.00 5.11 31.67 0.40 48.60
Device r/s w/s rKB/s wKB/s avgrq-sz avgqu-sz await r_await w_await %util
vda 35.0 120.0 980.0 6200.0 86.2 5.90 42.10 18.40 49.20 96.50
Significado: await en 42ms y %util cerca de 100% significa que el disco está saturado. Las escrituras son especialmente lentas.
Decisión: Reduce la presión de escritura (batching, índices, tuning de checkpoints), o mejora el nivel de almacenamiento. En un VPS, a veces la solución correcta es “pagar por mejor I/O”.
Tarea 3: Ver si estás haciendo swap (el asesino silencioso)
cr0x@server:~$ free -m
total used free shared buff/cache available
Mem: 3940 3605 45 60 289 110
Swap: 2047 1380 667
Significado: El swap se está usando activamente y la memoria available es mínima. Si la BD está intercambiando, cada petición se convierte en una operación de almacenamiento.
Decisión: Reduce el tamaño del caché de la base de datos, reduce la concurrencia de la aplicación o añade RAM. Si no haces nada, tu “tuning de base de datos” se convierte en “tuning de paginación de Linux”.
Tarea 4: Comprueba OOM kills
cr0x@server:~$ journalctl -k --since "2 hours ago" | tail -n 20
Dec 30 09:41:12 server kernel: Out of memory: Killed process 2145 (mongod) total-vm:5064820kB, anon-rss:2860100kB, file-rss:0kB, shmem-rss:0kB, UID:110 pgtables:7012kB oom_score_adj:0
Dec 30 09:41:12 server kernel: oom_reaper: reaped process 2145 (mongod), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
Significado: El kernel mató mongod. Esto no es un “bug de MongoDB”. Es una falla de capacidad y dimensionamiento.
Decisión: Inmediatamente reduce el uso de caché, corrige límites de memoria y añade margen. Luego revisa si este VPS puede alojar la carga de forma segura.
Tarea 5: Confirma qué proceso consume memoria
cr0x@server:~$ ps -eo pid,comm,rss,pcpu --sort=-rss | head
2145 mongod 2923400 88.2
1320 node 420800 12.1
901 mysqld 210500 6.8
755 redis-server 80400 1.2
Significado: MongoDB domina el RSS. En una caja pequeña, eso puede dejar sin recursos a todo lo demás.
Decisión: Si MongoDB es necesario, limita la caché de WiredTiger. Si es opcional, reconsidera la arquitectura en lugar de “pelear contra la física”.
Tarea 6: Comprobación MongoDB: presión de caché de WiredTiger
cr0x@server:~$ mongosh --quiet --eval 'db.serverStatus().wiredTiger.cache'
{
"bytes currently in the cache" : 1702453248,
"maximum bytes configured" : 2147483648,
"tracked dirty bytes in the cache" : 392154112,
"pages evicted by application threads" : 18342,
"pages queued for eviction" : 742,
"eviction server candidate queue empty when topping up" : 0
}
Significado: La caché está cerca del máximo y la actividad de expulsión es alta. Los bytes sucios son sustanciales, lo que implica presión de escritura y trabajo de checkpoint.
Decisión: Si el conjunto de trabajo no cabe, o añades RAM, o reduces documentos/índices, o aceptas almacenamiento más lento. El tuning no convertirá un VPS de 4GB en un caché de 64GB.
Tarea 7: Comprobación MongoDB: identificar operaciones lentas vía profiler
cr0x@server:~$ mongosh --quiet --eval 'db.setProfilingLevel(1, { slowms: 50 })'
{ "was" : 0, "slowms" : 50, "sampleRate" : 1, "ok" : 1 }
cr0x@server:~$ mongosh --quiet --eval 'db.system.profile.find().sort({ts:-1}).limit(3).pretty()'
{
"op" : "query",
"ns" : "app.events",
"command" : { "find" : "events", "filter" : { "userId" : "u_123" }, "sort" : { "ts" : -1 }, "limit" : 50 },
"keysExamined" : 0,
"docsExamined" : 51234,
"millis" : 231,
"planSummary" : "COLLSCAN"
}
Significado: COLLSCAN más un enorme docsExamined es un índice faltante/incorrecto. En un VPS, los escaneos de colección son la forma de calentar el centro de datos.
Decisión: Añade un índice que coincida con filtro+orden (p. ej., { userId: 1, ts: -1 }) y verifica con explain(). Si los índices crecen más allá de la RAM, igualmente perderás: planifica capacidad.
Tarea 8: Comprobación MongoDB: operaciones actuales y esperas por bloqueo
cr0x@server:~$ mongosh --quiet --eval 'db.currentOp({ "secs_running": { "$gte": 2 } }).inprog.map(o => ({op:o.op, ns:o.ns, secs:o.secs_running, waiting:o.waitingForLock, desc:o.desc}))'
[
{
"op" : "command",
"ns" : "app.events",
"secs" : 18,
"waiting" : true,
"desc" : "conn1421"
}
]
Significado: Operaciones de larga duración esperando por bloqueos suelen ser causadas por unas pocas escrituras pesadas, actualizaciones mal acotadas o índices construidos en el momento equivocado.
Decisión: Detén la hemorragia: pausa jobs en segundo plano, limita la tasa de escritores, reprograma construcciones de índices y encuentra el patrón de colección/documento caliente.
Tarea 9: Comprobación MySQL: estado global del buffer pool y lecturas
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';"
+---------------------------------------+-----------+
| Variable_name | Value |
+---------------------------------------+-----------+
| Innodb_buffer_pool_pages_total | 131072 |
| Innodb_buffer_pool_pages_free | 128 |
| Innodb_buffer_pool_read_requests | 983420112 |
| Innodb_buffer_pool_reads | 8420132 |
+---------------------------------------+-----------+
Significado: La proporción de read_requests a reads sugiere una tasa de aciertos decente, pero 8.4M lecturas de disco aún pueden ser dolorosas en almacenamiento débil.
Decisión: Si las lecturas se correlacionan con latencia, aumenta el buffer pool (si la memoria lo permite) o reduce la huella de dataset/índices. Si la memoria es escasa, arregla consultas e índices primero.
Tarea 10: Comprobación MySQL: encuentra principales esperas y sentencias calientes
cr0x@server:~$ mysql -e "SELECT event_name, count_star, sum_timer_wait/1000000000000 AS total_seconds FROM performance_schema.events_waits_summary_global_by_event_name ORDER BY sum_timer_wait DESC LIMIT 5;"
+--------------------------------------+------------+--------------+
| event_name | count_star | total_seconds|
+--------------------------------------+------------+--------------+
| wait/io/file/innodb/innodb_log_file | 182341 | 812.3 |
| wait/io/file/innodb/innodb_data_file | 491020 | 504.1 |
| wait/synch/mutex/innodb/buf_pool | 12034011 | 220.7 |
| wait/io/file/sql/binlog | 320114 | 118.9 |
| wait/lock/table/sql/handler | 80012 | 62.2 |
+--------------------------------------+------------+--------------+
Significado: Esperas intensas en archivos de log a menudo significan presión de fsync: demasiadas transacciones pequeñas, disco lento o ajustes de durabilidad que fuerzan sincronizaciones frecuentes.
Decisión: Considera agrupar escrituras, ajustar innodb_log_file_size y revisar los trade-offs de innodb_flush_log_at_trx_commit. No “optimices” la durabilidad sin una decisión empresarial.
Tarea 11: Comprobación MySQL: identificar consultas lentas
cr0x@server:~$ sudo tail -n 5 /var/log/mysql/mysql-slow.log
# Time: 2025-12-30T09:52:41.123456Z
# Query_time: 2.184 Lock_time: 0.000 Rows_sent: 50 Rows_examined: 981230
SELECT * FROM events WHERE user_id='u_123' ORDER BY ts DESC LIMIT 50;
Significado: Muchas filas examinadas para un pequeño conjunto resultante: falta un índice compuesto, o existe índice pero no puede soportar el orden.
Decisión: Añade o corrige el índice (p. ej., (user_id, ts)), luego verifica con EXPLAIN.
Tarea 12: Comprobación MySQL: verifica el plan con EXPLAIN
cr0x@server:~$ mysql -e "EXPLAIN SELECT * FROM events WHERE user_id='u_123' ORDER BY ts DESC LIMIT 50\G"
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: events
partitions: NULL
type: ALL
possible_keys: idx_user_ts
key: NULL
key_len: NULL
ref: NULL
rows: 980000
filtered: 10.00
Extra: Using where; Using filesort
Significado: type: ALL significa escaneo completo; Using filesort significa trabajo extra de ordenación. MySQL te está diciendo exactamente por qué es lento.
Decisión: Crea el índice compuesto correcto y confirma que key se usa. Si sigue escaneando, tus tipos de datos/collations o la forma de la consulta podrían bloquear el uso del índice.
Tarea 13: Comprueba acumulación de conexiones (la app puede DoSear la BD)
cr0x@server:~$ ss -s
Total: 941 (kernel 0)
TCP: 811 (estab 640, closed 121, orphaned 0, synrecv 0, timewait 121/0), ports 0
Transport Total IP IPv6
RAW 0 0 0
UDP 7 6 1
TCP 690 651 39
INET 697 657 40
FRAG 0 0 0
Significado: Cientos de conexiones TCP establecidas pueden indicar una mala configuración del pool de conexiones o una manada de procesos durante reintentos.
Decisión: Limita la concurrencia de la app, usa un tamaño de pool sensato y considera un proxy (para MySQL) si el churn de conexiones es alto.
Tarea 14: Comprueba espacio en filesystem y presión de inodos
cr0x@server:~$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 60G 55G 2.1G 97% /
cr0x@server:~$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 3932160 3919000 13160 100% /
Significado: El disco está casi lleno y los inodos están agotados. Las bases de datos odian esto. También los logs. Todo lo odia.
Decisión: Libera espacio inmediatamente (rota logs, borra backups antiguos), luego añade monitorización y políticas de retención para que nunca vuelvas a alcanzar 97%.
Tarea 15: Eliminar la “latencia misteriosa” midiendo el coste de fsync
cr0x@server:~$ sudo dd if=/dev/zero of=/var/lib/db/fsync-test bs=4k count=4096 conv=fdatasync
4096+0 records in
4096+0 records out
16777216 bytes (17 MB, 16 MiB) copied, 1.84213 s, 9.1 MB/s
Significado: 16MiB con fdatasync tomando ~1.8s es una señal roja para cargas con escrituras durables. Tu base de datos sentirá esto como latencia de commit.
Decisión: Si tu workload es intensivo en escritura, muévete a mejor almacenamiento (NVMe con latencia predecible) o reduce la frecuencia de fsync mediante batching. No lo arregles desactivando durabilidad a menos que el negocio acepte explícitamente pérdida de datos.
Tarea 16: Verifica ajustes de durabilidad de MySQL antes de “optimizar”
cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_flush_log_at_trx_commit','sync_binlog','innodb_doublewrite');"
+------------------------------+-------+
| Variable_name | Value |
+------------------------------+-------+
| innodb_doublewrite | ON |
| innodb_flush_log_at_trx_commit | 1 |
| sync_binlog | 1 |
+------------------------------+-------+
Significado: Estos ajustes priorizan la durabilidad. Eso es bueno—hasta que tu disco no pueda seguir el ritmo.
Decisión: Si la latencia de commits es el cuello de botella, primero aborda el I/O y el batching. Solo entonces considera relajar ajustes con aprobación clara de las partes interesadas.
Chiste #2: “Elegimos NoSQL por rendimiento” es como decir que compraste una camioneta para ganar una carrera de bicicletas.
Errores comunes: síntomas → causa raíz → solución
Estos no son teóricos. Son los patrones que aparecen en VPS reales a las 3 a. m.
1) Síntoma: p95 de latencia se dispara cada pocos minutos
Causa raíz: Tormentas de checkpoint/flush. Los checkpoints de MongoDB o el vaciado de páginas sucias de MySQL golpean un disco saturado.
Solución: Reduce ráfagas de escritura (batch, queue), asegura espacio libre en disco, aumenta RAM para caché y muévete a almacenamiento de menor latencia. Para MySQL, revisa tamaños de logs y comportamiento de flushing; para MongoDB, vigila eviction y timing de checkpoints.
2) Síntoma: MongoDB “rápido en dev, lento en prod”
Causa raíz: El dataset de dev cabe en RAM. El de prod no. Las tasas de fallo de caché explotan; los índices no caben; el disco se convierte en la base de datos.
Solución: Carga datos realistas en staging. Mide el conjunto de trabajo. Impone disciplina de tamaño de documento y crea índices para consultas reales, no imaginadas.
3) Síntoma: MySQL CPU alta, pero el disco parece bien
Causa raíz: Consultas con muchas funciones, extracción de JSON en rutas calientes o índices faltantes que provocan escaneos y ordenaciones costosas en CPU.
Solución: Usa EXPLAIN, añade índices adecuados, mueve campos calculados a columnas almacenadas cuando sea necesario y deja de ordenar grandes conjuntos sin índices.
4) Síntoma: OOM kills o reinicios aleatorios de la base de datos
Causa raíz: Dimensionamiento de caché supone más RAM de la que tienes; fragmentación de memoria; servicios adicionales en el mismo VPS; espiral de swap.
Solución: Limita cachés (WiredTiger e InnoDB), reserva RAM para el SO y la app, y reduce cargas co-localizadas. Si necesitas la BD, dale la máquina adecuada.
5) Síntoma: “Añadimos un índice y fue más lento”
Causa raíz: Índice incrementa la amplificación de escritura; construcciones de índices en segundo plano compiten por I/O; el índice no coincide con la forma de la consulta y queda sin usar.
Solución: Confirma el uso (EXPLAIN o explain() de MongoDB). Construye índices fuera de horas pico. Usa el conjunto mínimo de índices que soporte consultas reales.
6) Síntoma: La latencia de replicación crece durante picos de tráfico
Causa raíz: El primario está saturado; las secundarias no pueden aplicar ops lo suficientemente rápido; el disco es cuello de botella; demasiadas transacciones pequeñas.
Solución: Mejora el I/O del primario, agrupa escrituras, asegura que las secundarias tengan rendimiento de almacenamiento similar y evita transacciones largas que retrasen la aplicación.
7) Síntoma: “MongoDB usa toda la RAM; eso debe ser malo”
Causa raíz: Confundir uso de caché con fuga de memoria. Las bases de datos quieren memoria; el problema es cuando la roban al SO y disparan swap/OOM.
Solución: Establece límites de caché explícitos y deja espacio para la caché de sistema y otros procesos. Memoria sin margen es solo tiempo de inactividad futuro.
Listas de verificación / plan paso a paso
Lista de decisión: ¿debería este workload usar MySQL o MongoDB en un VPS?
- Elige MySQL si necesitas joins, restricciones estrictas, esquema estable, actualizaciones transaccionales entre entidades o rendimiento predecible con poca RAM.
- Elige MongoDB si el patrón de acceso principal es centrado en documentos, tus documentos permanecen acotados, tus índices caben en memoria y puedes ejecutarlo con disciplina de replica set.
- No elijas ninguno a ciegas: modela las rutas calientes y mide el conjunto de trabajo frente a la RAM. Si no puedes hacer eso, estás apostando.
Paso a paso: estabilizar una base de datos en un VPS en 60–120 minutos
- Detén la hemorragia: limita la tasa de escritores pesados, pausa jobs no críticos, reduce la concurrencia de peticiones temporalmente.
- Confirma el cuello de botella:
vmstat+iostat+ comprobaciones de memoria. - Encuentra a los principales ofensores: log lento de MySQL / performance_schema; profiler/currentOp de MongoDB.
- Arregla la peor consulta primero: añade el índice correcto o reescribe la consulta para usar uno existente.
- Limita cachés de forma segura: deja RAM para el SO y la app; evita swap.
- Verifica salud y espacio del disco: discos casi llenos se comportan mal; agotamiento de inodos es una caída silenciosa.
- Re-prueba bajo carga: confirma que p95 y
awaitdel disco mejoran; vigila nuevos cuellos de botella. - Escribe la línea base: tasas de acierto, latencia de disco, conteo de conexiones y ops/sec típicos. Esto será tu “diagnóstico rápido” futuro.
Paso a paso: prevenir la falla de “NoSQL porque está de moda”
- Escribe 5 consultas reales que tu app ejecutará cada segundo y cada minuto.
- Para MongoDB: define límites de documento y reglas máximas de crecimiento de documentos.
- Para MySQL: define índices para las rutas calientes y mantiene columnas calientes tipadas e indexadas (no enterradas en JSON).
- Carga datos realistas en staging y fuerza un escenario de fallo de caché restringiendo RAM o usando una instancia más pequeña.
- Mide la latencia de fsync del disco y verifica que coincide con tus expectativas de durabilidad.
- Practica restauraciones en un calendario. Tu yo futuro no tendrá tiempo para aprender durante un outage.
Preguntas frecuentes
1) ¿MongoDB siempre es más lento que MySQL en un VPS?
No. MongoDB puede ser muy rápido cuando tu patrón de acceso es centrado en documentos y tu conjunto de trabajo cabe en RAM. El fallo habitual en VPS es “el dataset creció y el caché no”.
2) ¿Por qué el rendimiento de MongoDB cae en picado cuando los datos crecen?
Porque los fallos de caché se convierten en lecturas de disco, y la sobrecarga de documento/índice puede inflar el conjunto de trabajo. Cuando la presión de eviction aumenta en almacenamiento lento, la latencia se dispara.
3) ¿Puedo simplemente aumentar la caché de WiredTiger y listo?
No de forma segura en un VPS pequeño. Si dejas sin recursos al SO y a otros servicios, cambias “consultas lentas” por tormentas de swap y OOM kills. Límitala deliberadamente y deja margen.
4) ¿Es MySQL “mejor” porque es relacional?
Es mejor cuando tus datos necesitan relaciones, restricciones y consultas indexadas predecibles. Es peor cuando tu aplicación es verdaderamente con forma de documento y tendrías que hacer joins incómodos.
5) ¿Por qué la gente dice “NoSQL escala”?
Históricamente, muchos sistemas NoSQL se diseñaron con distribución horizontal como objetivo principal. Pero escalar no es automático, y en un VPS pequeño no estás escalando horizontalmente nada.
6) ¿Cuál es el mayor error de esquema de MongoDB en VPS?
Documentos no acotados (arrays que crecen para siempre, estructuras profundamente anidadas o “almacena todo el historial en un doc”). Parece elegante hasta que se convierte en un objeto enorme y caliente que destruye la localidad de caché y el comportamiento de escrituras.
7) ¿Cuál es el mayor error de MySQL en VPS?
Ejecutarlo con configuraciones por defecto que asumen más RAM e I/O del que tienes, además de índices compuestos faltantes. Luego intentas “arreglarlo” añadiendo CPU, lo que no arregla los parones por disco.
8) ¿Debería desactivar fsync/ajustes de durabilidad para recuperar rendimiento?
Solo si el negocio acepta explícitamente pérdida de datos y documentas el riesgo. La mayoría de los “durability off” son curitas temporales que convierten el siguiente incidente en teatro de recuperación de datos.
9) Si mi app usa mucho JSON, ¿eso significa que MongoDB es la elección obvia?
No. JSON como formato de intercambio no implica que una base documental sea óptima. MySQL puede almacenar e indexar columnas estructuradas y mantener algo de JSON para campos fríos.
10) ¿Cuál es la estrategia más simple y segura para un VPS pequeño?
Una base de datos, un trabajo, suficiente RAM para caché y almacenamiento predecible. Si no puedes permitir eso, tampoco puedes permitir complejidad sorpresa: elige el motor más simple que encaje con el modelo de datos.
Conclusión: próximos pasos que realmente puedes hacer
El error de “NoSQL porque está de moda” no es elegir MongoDB. Es elegirlo sin entender qué puede pagar tu VPS: fallos de RAM, fsyncs, compactación y eviction de caché.
MySQL suele ser el ganador aburrido en cajas pequeñas porque es compacto, predecible y fácil de tunear para patrones OLTP comunes. MongoDB puede ser una excelente opción cuando tus documentos están acotados y tus índices caben—especialmente si diseñas en torno a consultas reales, no sueños.
Próximos pasos prácticos:
- Ejecuta la guía rápida de diagnóstico una vez en período tranquilo y registra la línea base de
iostat, memoria y estadísticas de caché de la BD. - Elige tus 5 consultas principales y demuestra que usan índices (
EXPLAINoexplain()de MongoDB). - Establece límites de caché explícitos para que el SO nunca tenga que “negociar” con el swap.
- Prueba una restauración. No “tenemos backups”. Una restauración real a una máquina limpia.
- Si
awaitdel disco está consistentemente alto, deja de tunear alrededor y mejora el almacenamiento o mueve la BD fuera del VPS.