No eliges una base de datos en el vacío. La eliges a las 2:13 a.m., con el teléfono de guardia vibrando, una cola creciendo
y alguien preguntando por qué “la base de datos” está lenta como si fuera un único aparato que puedes reiniciar.
Si estás decidiendo entre MySQL y TiDB, en realidad estás decidiendo entre una herramienta grande, familiar y afilada y
un sistema distribuido que promete parecer familiar. Ambos pueden funcionar. Ambos pueden hacerte daño. La diferencia es
qué tipo de dolor quieres: el techo de escalado y el trabajo manual de particionado de MySQL, o la superficie operativa y
los modos de fallo distribuidos de TiDB.
La decisión que realmente importa
La mayoría de los debates “MySQL vs TiDB” fingen que la pregunta trata de funciones. No es así. Se trata de quién hace
el trabajo de la complejidad.
-
MySQL empuja la complejidad hacia arriba, a tu aplicación y a tu equipo: estrategia de sharding, consultas entre shards,
escrituras dobles, generación de IDs, cambios de esquema con mínima interrupción, gestión de topologías de replicación,
lógica de separación lectura/escritura y la alegría de descubrir que tu join “simple” ahora es un join distribuido entre shards. -
TiDB tira la complejidad hacia abajo, dentro de la plataforma de base de datos: reglas de colocación, replicación Raft,
splits/fusiones de regiones, planificación por PD, coordinación de transacciones distribuidas, hotspots y actualizaciones de múltiples componentes.
Tu código de aplicación queda más simple. Tus runbooks de guardia se vuelven más gruesos.
He aquí la heurística práctica que uso:
-
Si puedes escalar MySQL con réplicas, caching, indexación cuidadosa y escalados verticales ocasionales —y tu mayor
dolor es la disciplina operativa— quédate con MySQL. Es aburrido, probado y tu equipo ya sabe cómo falla. -
Si has llegado al punto donde el sharding es inevitable (o ya es un desastre), y necesitas consistencia fuerte sobre
un conjunto de datos grande con escalado horizontal elástico, TiDB es una apuesta razonable—pero solo si puedes dotarlo
de personal y operarlo como una plataforma, no como “un nodo de BD con amigos.”
Y sí, TiDB habla el protocolo MySQL. Eso es el inicio de la compatibilidad, no la línea de llegada.
Hechos y contexto histórico que cambian tu intuición
Unos cuantos puntos de anclaje para mantener la mente honesta. No son trivia; explican por qué los sistemas se comportan como lo hacen.
-
La replicación de MySQL empezó como replicación asíncrona basada en sentencias. Ese legado explica por qué
“lag de replicación” y los workarounds de “leer después de escribir” se volvieron conocimiento cultural común para los equipos MySQL. -
InnoDB se convirtió en el motor de almacenamiento por defecto en MySQL 5.5. Antes de eso, MyISAM era común, y
esa era inculcó la idea de “MySQL es rápido pero no seguro” en la memoria colectiva de Internet. -
Percona y MariaDB surgieron porque los operadores querían mejor observabilidad y controles operativos.
La historia del ecosistema MySQL es básicamente “los SRE exigieron herramientas y nació una industria.” -
Spanner de Google (2012) marcó expectativas para “SQL + escala horizontal + consistencia fuerte.”
TiDB está en la familia de sistemas que hizo ese sueño más accesible sin requerir relojes atómicos. -
La capa de almacenamiento de TiDB (TiKV) está construida sobre RocksDB y replicación Raft. Eso significa que el rendimiento y
los modos de fallo se parecen más a un almacén de claves-valor distribuido que a un motor B-tree de un solo nodo. -
TiDB divide los datos en Regiones (rangos) y las planifica mediante PD. Ese reequilibrio de regiones es tanto
una superpotencia como una fuente de momentos “¿por qué me subió la latencia?” -
GTID y la replicación semi-síncrona de MySQL fueron respuestas a dolores reales. Mejoraron la confiabilidad operativa, pero no convierten a MySQL
en una base de datos SQL distribuida; solo reducen el radio de explosión del error humano. -
Las herramientas de cambio de esquema en línea (gh-ost, pt-online-schema-change) se hicieron mainstream porque el DDL de MySQL era históricamente disruptivo.
En TiDB, muchos cambios de esquema son en línea, pero aun así pagan en trabajo de clúster y efectos downstream. - “Compatible con MySQL” es un objetivo en movimiento. MySQL en sí tiene múltiples versiones mayores, forks de proveedor y diferencias de comportamiento. La compatibilidad no es binaria; es una matriz.
MySQL en producción: qué se rompe, qué escala, qué son mitos
En qué MySQL es realmente bueno
MySQL es una opción sólida cuando la carga encaja en un primario único (o un pequeño número de primarios) con réplicas.
Es sencillo de operar si lo mantienes simple: un escritor, muchos lectores; consultas predecibles; esquema estable;
configuraciones conservadoras; y un equipo que respeta la base de datos como un recurso compartido, no como una máquina expendedora infinita.
Para muchas empresas, el “techo” de MySQL es mucho más alto de lo que creen—porque el verdadero cuello de botella eran índices faltantes,
transacciones enormes, ORMs excesivamente conversadores, o una cola de trabajo que hacía full table scans como si cobrara por fila.
Dónde MySQL te hace pagar
La historia de escalado de MySQL para cargas intensivas en escritura es básicamente: escala vertical, afina más, luego shardear. Sharding funciona,
pero es un compromiso. Reescribirás suposiciones:
- Las transacciones entre shards se convierten en coordinación en la aplicación o en consistencia eventual.
- La unicidad global requiere IDs que no colisionen entre shards.
- Las consultas de reporting se convierten en jobs ETL o almacenes analíticos.
- El trabajo operativo se desplaza a gestionar muchas bases de datos más pequeñas, backups y migraciones de esquema.
Los modos de fallo de MySQL son bien entendidos: lag de replicación, contención de locks, presión en el buffer pool, stalls de IO de disco,
y “una consulta arruinó el día de todos.” La ventaja es que la mayoría de los ingenieros ya los han visto antes. La desventaja es
que los equipos a menudo los normalizan y dejan de cuestionar decisiones arquitectónicas.
Exactamente un chiste, como prometí: MySQL es como un cuchillo de cocina—excelente hasta que alguien decide usarlo también como destornillador.
TiDB en producción: qué ganas, qué heredas
En qué TiDB es realmente bueno
La propuesta de TiDB es atractiva porque aborda el clásico dolor de MySQL: escalar sin sharding manual manteniendo SQL y transacciones. En la práctica, las victorias son reales cuando:
- Tu conjunto de datos y throughput de escrituras exceden un primario único y quieres escalado horizontal.
- Necesitas semánticas de consistencia fuerte sin construir tu propia lógica cross-shard.
- Puedes tolerar la sobrecarga operativa de correr un sistema distribuido.
- Necesitas crecer en capacidad añadiendo nodos en lugar de planear upgrades “caja grande”.
Lo que TiDB te hace pagar
TiDB no es “MySQL, pero más rápido.” Es una base de datos distribuida con una interfaz compatible con MySQL. Ahora administras:
- TiDB capa SQL sin estado (escala horizontal).
- TiKV capa de almacenamiento con estado (replicada por Raft, basada en regiones).
- PD placement driver (cerebro del clúster para planificación y metadatos).
- A menudo TiFlash para aceleración analítica y cargas HTAP.
Cada componente tiene sus propios puntos de saturación, métricas, rutas de upgrade y estados “técnicamente sano pero efectivamente degradado”.
TiDB facilita el escalado; no simplifica las operaciones. Traslada la complejidad del sharding en la capa de aplicación a la planificación
y coordinación en la capa de plataforma.
También estás optando por un tipo diferente de ajuste de rendimiento: menos “nostalgia de tamaño de buffer pool y query cache,”
más “hotspots de regiones, tareas coprocessor y latencia en raftstore.”
Compatibilidad con MySQL: la letra pequeña que muerde
La compatibilidad de protocolo no es compatibilidad de comportamiento
Muchas migraciones empiezan con “nuestra app habla MySQL, así que TiDB debería ser drop-in.” Eso es cierto hasta que tu app
depende de comportamientos en casos límite. La mayoría de las apps lo hacen, solo que aún no lo saben.
Las brechas de compatibilidad aparecen en cuatro lugares prácticos:
-
Gramática SQL y funciones: algunas funciones difieren, otras faltan y algunas se comportan sutilmente
diferente en casos límite. - Transacciones y semánticas de bloqueo: las transacciones distribuidas no se comportan exactamente como el bloqueo de un solo nodo bajo contención.
- DDL y metadatos: el “DDL en línea” es genial hasta que genera carga de fondo que no presupuestaste.
-
Suposiciones operativas: en MySQL, a veces puedes “simplemente reiniciar mysqld” para limpiar un estado atascado.
En TiDB, reiniciar un componente puede desencadenar replanificación o elecciones de líder—está bien, pero no es gratuito.
Consistencia: la simplicidad de MySQL vs la coordinación de TiDB
MySQL con un primario único ofrece un modelo mental claro: el primario decide. Las réplicas siguen. La consistencia fuerte
es local al primario; las lecturas desde réplicas son “posiblemente obsoletas”, y todos aprenden a convivir con ello.
TiDB apunta a consistencia fuerte en todo el clúster distribuido. Eso significa:
- Las escrituras deben alcanzar un quórum de réplicas para los grupos Raft (Regiones).
- Las transacciones pueden involucrar múltiples Regiones, potencialmente en distintos nodos.
- El jitter de red y nodos de almacenamiento lentos se convierten en latencia de consulta de maneras nuevas y creativas.
Sorpresas de rendimiento: la “tasa distributiva”
Algunas consultas son más rápidas en TiDB porque puedes escalar cómputo y almacenamiento. Otras son más lentas porque una consulta que
era una búsqueda local por índice en MySQL se convierte en una operación distribuida que involucra múltiples Regiones.
Aquí es donde necesitas ser brutalmente honesto sobre la forma de la carga de trabajo:
- Búsquedas de una fila por clave primaria pueden ir muy bien, pero se pueden formar hotspots si muchos clientes golpean el mismo rango de claves.
- Escaneos de rango grandes pueden ser decentes si se paralelizan, pero también pueden desencadenar una carga intensa en el coprocessor.
- Joins son donde debes prestar atención. La planificación de joins distribuidos y la calidad de las estadísticas importan más.
Complejidad operativa: lo que TiDB añade a tu pager
Estás operando una plataforma de base de datos
Con MySQL, la unidad de operación es un servidor (o un clúster con un primario). Con TiDB, la unidad de operación es un sistema. Tendrás que gestionar:
- Actualizaciones rolling en múltiples componentes
- Gestión de capacidad entre cómputo, almacenamiento y red
- Dominios de fallo (racks/Zonas), reglas de colocación y conteo de réplicas
- Detección y mitigación de hotspots
- Backups/restauraciones que respeten la consistencia distribuida
- Cambios de esquema que desencadenan reorganización de datos en segundo plano
La observabilidad no es opcional
En MySQL puedes avanzar sorprendentemente con slow query logs, performance_schema y métricas básicas del host. En TiDB,
necesitas observabilidad a nivel de clúster: salud de PD, latencia de stores TiKV, balance de regiones, duración de apply de raft y saturación de la capa SQL.
Aquí está la verdad operativa: si no tienes buenas métricas y alertas, TiDB seguirá funcionando—hasta que deje de hacerlo—y
entonces tu línea temporal de incidentes parecerá arte abstracto.
Una cita (idea parafraseada)
Idea parafraseada: Si no puedes medirlo, no puedes mejorarlo de forma fiable.
— atribuida a W. Edwards Deming
Exactamente una cita, porque así funciona la fiabilidad: alcance limitado, propiedad clara.
Tareas prácticas con comandos, salidas y decisiones (manos a la obra)
La forma más rápida de entender la complejidad operativa es tocarla. A continuación hay tareas concretas que esperaría que un SRE o
DBRE ejecute durante la planificación de una migración o un incidente. Cada una incluye: un comando, salida de muestra, qué significa y
qué decisión tomar a partir de ello.
Tarea 1: Comprobar el lag de replicación de MySQL (¿son confiables las lecturas?)
cr0x@server:~$ mysql -h mysql-replica-01 -e "SHOW REPLICA STATUS\G" | egrep "Seconds_Behind_Source|Replica_IO_Running|Replica_SQL_Running"
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 37
Significado: la réplica está sana pero ~37s por detrás. Cualquier “leer después de escribir” desde esta réplica puede estar obsoleta.
Decisión: enrutar lecturas críticas al primario (o a una réplica de bajo lag), o arreglar el lag antes de usar réplicas para rutas sensibles a corrección.
Tarea 2: Encontrar los principales eventos de espera en MySQL (¿son locks, IO o CPU?)
cr0x@server:~$ mysql -e "SELECT EVENT_NAME, SUM_TIMER_WAIT/1e12 AS seconds_waited FROM performance_schema.events_waits_summary_global_by_event_name ORDER BY SUM_TIMER_WAIT DESC LIMIT 5;"
EVENT_NAME seconds_waited
wait/io/file/innodb/innodb_data_file 1843.22
wait/synch/mutex/innodb/buf_pool_mutex 912.45
wait/lock/table/sql/handler 211.10
wait/io/file/sql/binlog 199.08
wait/synch/cond/sql/MYSQL_BIN_LOG::COND_done 133.57
Significado: IO pesado en archivos de datos InnoDB y contención del buffer pool. No estás “ligado a CPU”; estás esperando.
Decisión: revisar latencia de almacenamiento, dimensionamiento del buffer pool y tablas calientes. Si esto es crónico a tu escala, TiDB podría ayudar—si tu carga se comporta como shards.
Tarea 3: Identificar consultas MySQL con peor latencia en el percentil 95
cr0x@server:~$ pt-query-digest /var/log/mysql/mysql-slow.log --limit 3
# 1.2s user time, 60ms system time, 22.18M rss, 221.20M vsz
# Rank Query ID Response time Calls R/Call V/M Item
# ==== ================== ============= ===== ====== ===== ====
# 1 0x8D7A... 521.7340 38.4% 120 4.3478 0.02 SELECT orders
# 2 0x44B1... 301.0092 22.1% 560 0.5375 0.01 UPDATE inventory
# 3 0xA1E3... 198.5521 14.6% 45 4.4123 0.00 SELECT users
Significado: dos SELECT lentos dominan el tiempo de respuesta; un UPDATE también es costoso y frecuente.
Decisión: afinar esquema y consultas primero. Migrar una consulta lenta a TiDB solo la hará lenta en más servidores.
Tarea 4: Inspeccionar tamaño de tabla/índice en MySQL (¿estás limitado por IO debido a bloat?)
cr0x@server:~$ mysql -e "SELECT table_schema, table_name, ROUND((data_length+index_length)/1024/1024/1024,2) AS gb FROM information_schema.tables WHERE table_schema='app' ORDER BY (data_length+index_length) DESC LIMIT 5;"
table_schema table_name gb
app events 412.77
app orders 188.34
app order_items 141.02
app users 62.11
app inventory 51.89
Significado: tienes algunas tablas muy grandes. Esas dominan IO y la presión del buffer pool.
Decisión: considerar particionado, archivado o almacenamiento secundario para datos fríos. Si el acceso es amplio y con muchas escrituras, el almacenamiento horizontal de TiDB puede ayudar.
Tarea 5: Comprobar salud del clúster TiDB (¿la plataforma es estable?)
cr0x@server:~$ tiup cluster display tidb-prod
Cluster type: tidb
Cluster name: tidb-prod
Cluster version: v7.5.1
ID Role Host Ports OS/Arch Status Data Dir
-- ---- ---- ----- ------- ------ --------
tidb-01 tidb 10.0.2.11 4000/10080 linux/x86_64 Up /tidb-data/tidb-4000
pd-01 pd 10.0.2.21 2379/2380 linux/x86_64 Up /tidb-data/pd-2379
tikv-01 tikv 10.0.3.31 20160/20180 linux/x86_64 Up /tidb-data/tikv-20160
tikv-02 tikv 10.0.3.32 20160/20180 linux/x86_64 Up /tidb-data/tikv-20160
tikv-03 tikv 10.0.3.33 20160/20180 linux/x86_64 Up /tidb-data/tikv-20160
Significado: todos los componentes están arriba. Esto no garantiza rendimiento, pero descarta “nodo caído” como problema de primer orden.
Decisión: si algún TiKV o PD está caído, deja de afinar SQL y arregla la salud del clúster primero.
Tarea 6: Verificar salud y líder de PD (¿el cerebro está confundido?)
cr0x@server:~$ tiup ctl:v7.5.1 pd -u http://10.0.2.21:2379 member
{
"members": [
{
"name": "pd-01",
"member_id": 1234567890,
"client_urls": ["http://10.0.2.21:2379"],
"peer_urls": ["http://10.0.2.21:2380"]
}
],
"leader": {
"name": "pd-01",
"member_id": 1234567890
}
}
Significado: PD es un solo miembro aquí; es el líder. En producción normalmente quieres múltiples nodos PD para HA.
Decisión: si PD no es HA, arregla la arquitectura antes de apostar la compañía en el clúster.
Tarea 7: Comprobar capacidad y balance de stores TiKV (¿estás a punto de caerte por un precipicio?)
cr0x@server:~$ tiup ctl:v7.5.1 pd -u http://10.0.2.21:2379 store
{
"count": 3,
"stores": [
{"store": {"id": 1, "address": "10.0.3.31:20160"}, "status": {"capacity": "3.6TiB", "available": "0.4TiB"}},
{"store": {"id": 2, "address": "10.0.3.32:20160"}, "status": {"capacity": "3.6TiB", "available": "0.3TiB"}},
{"store": {"id": 3, "address": "10.0.3.33:20160"}, "status": {"capacity": "3.6TiB", "available": "0.2TiB"}}
]
}
Significado: todos los stores tienen poco espacio libre. La compactación, snapshots y reequilibrio se vuelven más riesgosos con discos casi llenos.
Decisión: añade capacidad antes de afinar el rendimiento. “Casi lleno” en el mundo de RocksDB es cómo te programas tu propio outage.
Tarea 8: Encontrar consultas lentas en TiDB rápidamente (¿se trata de regresiones de plan?)
cr0x@server:~$ mysql -h tidb-01 -P 4000 -e "SELECT time, query_time, digest, left(query,120) AS sample FROM information_schema.cluster_slow_query ORDER BY query_time DESC LIMIT 3;"
time query_time digest sample
2025-12-30T09:40:11Z 8.214 9f2d... SELECT * FROM orders WHERE user_id=... ORDER BY created_at DESC LIMIT 50
2025-12-30T09:41:02Z 6.882 2a11... SELECT COUNT(*) FROM events WHERE created_at BETWEEN ... AND ...
2025-12-30T09:41:33Z 5.477 0bd3... UPDATE inventory SET qty=qty-1 WHERE sku=...
Significado: tienes consultas lentas similares a MySQL, pero ahora necesitas examinar la ejecución distribuida (cop tasks, regiones tocadas, etc.).
Decisión: sacar planes de ejecución y comprobar frescura de estadísticas. Si coincidió con un cambio de esquema o versión, sospecha regresión de plan.
Tarea 9: EXPLAIN en TiDB y detectar dolor distribuido (¿Índice? ¿coprocessor? ¿scatter?)
cr0x@server:~$ mysql -h tidb-01 -P 4000 -e "EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id=42 ORDER BY created_at DESC LIMIT 50\G"
*************************** 1. row ***************************
id: TopN_10
actRows: 50
task: root
execution info: time:1.2s, loops:2
operator info: order by:created_at, offset:0, count:50
*************************** 2. row ***************************
id: IndexLookUp_21
actRows: 5000
task: root
execution info: time:1.1s, loops:5
operator info: index:idx_user_created(user_id,created_at), table:orders
*************************** 3. row ***************************
id: IndexRangeScan_19
actRows: 5000
task: cop[tikv]
execution info: time:980ms, loops:8
operator info: range:[42,42], keep order:true
Significado: la mayor parte del tiempo se gasta en cop tasks sobre TiKV. Eso apunta a latencia de almacenamiento, hotspotting o demasiadas regiones involucradas.
Decisión: investigar latencia de TiKV, distribución de regiones para ese rango de índice y si la carga crea un hotspot en claves tipo user_id=42.
Tarea 10: Comprobar hotspots de regiones (¿un rango de claves está derritiendo un store?)
cr0x@server:~$ tiup ctl:v7.5.1 pd -u http://10.0.2.21:2379 hot read
{
"as_peer": {
"stats": [
{"store_id": 3, "region_id": 918273, "hot_degree": 97, "flow_bytes": 125829120}
]
}
}
Significado: el store 3 está sirviendo una región muy caliente para lecturas. Eso puede dominar la latencia tail.
Decisión: mitigar el hotspot: añadir nodos TiDB para escalar SQL, considerar dividir regiones, ajustar patrones de acceso de la aplicación o usar cache para ese rango caliente de claves.
Tarea 11: Comprobar latencia de TiKV vía endpoint de métricas de Prometheus (¿es el almacenamiento el cuello de botella?)
cr0x@server:~$ curl -s http://10.0.3.33:20180/metrics | egrep "tikv_engine_write_stall|rocksdb_compaction_pending_bytes" | head
tikv_engine_write_stall 1
rocksdb_compaction_pending_bytes 2.184532e+10
Significado: el write stall está activo y el backlog de compactación es enorme. RocksDB está retrasado; escrituras y lecturas sufrirán.
Decisión: deja de culpar a SQL. Arregla el rendimiento del disco, reduce la amplificación de escritura, revisa opciones de TiKV y considera añadir stores para repartir la carga de compactación.
Tarea 12: Validar sincronización horaria (a las transacciones distribuidas no les gustan los relojes descuidados)
cr0x@server:~$ chronyc tracking
Reference ID : C0A80101 (ntp-01)
Stratum : 3
System time : 0.000143219 seconds slow of NTP time
Last offset : -0.000012034 seconds
RMS offset : 0.000084321 seconds
Frequency : 10.123 ppm slow
Residual freq : -0.002 ppm
Skew : 0.090 ppm
Root delay : 0.001234 seconds
Root dispersion : 0.001901 seconds
Update interval : 64.0 seconds
Leap status : Normal
Significado: el reloj está bien sincronizado (offsets submilisegundo). Bien. Mala sincronización crea comportamientos extraños de transacciones y timeouts en sistemas distribuidos.
Decisión: si los offsets son grandes o el estado de salto es malo, arregla NTP/chrony antes de perseguir picos de latencia fantasma.
Tarea 13: Validar conflictos de transacciones en TiDB (¿estás luchando contra la contención?)
cr0x@server:~$ mysql -h tidb-01 -P 4000 -e "SHOW STATUS LIKE 'tikv_txn_mvcc_%conflict%';"
Variable_name Value
tikv_txn_mvcc_conflict_counter 18422
tikv_txn_mvcc_write_conflict_counter 12011
Significado: están ocurriendo conflictos. Bajo cargas con hot-rows, TiDB se comportará como cualquier sistema consistente: fuerza serialización en algún lugar.
Decisión: rediseñar patrones de hot-row (contadores, updates de “última vez visto”), agrupar escrituras o mover primitivas con alta contención a otro almacén.
Tarea 14: Comprobar latencia de disco en MySQL (¿el host te está mintiendo?)
cr0x@server:~$ iostat -x 1 3 | egrep "Device|nvme0n1"
Device r/s w/s r_await w_await aqu-sz %util
nvme0n1 210.4 980.2 3.12 18.44 9.87 99.10
nvme0n1 198.1 1050.6 2.88 21.77 11.22 99.54
nvme0n1 205.7 1012.3 3.05 19.90 10.45 99.33
Significado: %util al máximo; latencia de escritura ~20ms. Eso es suficiente para fastidiar tanto a MySQL como a TiKV.
Decisión: antes de migrar a cualquier cosa, arregla el almacenamiento: aislar cargas, actualizar discos, revisar configuraciones RAID/controlador y reducir amplificación de escritura.
Tarea 15: Comprobar retransmisiones de red (los sistemas distribuidos magnifican los pecados de la red)
cr0x@server:~$ netstat -s | egrep "segments retransmited|packet receive errors" | head
13245 segments retransmited
0 packet receive errors
Significado: hay retransmisiones; si es “malo” depende de la línea base y la ventana temporal, pero en TiDB puede aparecer como latencia de raft y picos p99.
Decisión: correlacionar con la ventana del incidente. Si las retransmisiones aumentan, investigar drops de NIC, sobre-subcripción o vecinos ruidosos.
Tarea 16: Confirmar saturación de nodo TiDB (el escalado horizontal de la capa SQL es una palanca)
cr0x@server:~$ top -b -n 1 | head -n 12
top - 09:45:11 up 34 days, 3:21, 1 user, load average: 24.12, 21.88, 19.05
Tasks: 312 total, 2 running, 310 sleeping, 0 stopped, 0 zombie
%Cpu(s): 92.1 us, 3.4 sy, 0.0 ni, 3.9 id, 0.0 wa, 0.0 hi, 0.6 si, 0.0 st
MiB Mem : 64384.0 total, 1200.3 free, 45210.8 used, 17972.9 buff/cache
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22411 tidb 20 0 14.1g 3.2g 112m S 860.2 5.1 12:11.43 tidb-server
Significado: nodo SQL de TiDB está caliente en CPU. Dado que TiDB es stateless en la capa SQL, a menudo puedes escalar horizontalmente este tier.
Decisión: añadir nodos TiDB o reducir consultas costosas; no culpes inmediatamente a TiKV si TiDB ya está al rojo vivo.
Guía de diagnóstico rápido (primera/segunda/tercera comprobación)
Cuando la latencia sube o el throughput cae, quieres identificar el cuello de botella sin reinventar la teoría de sistemas distribuidos
en una pizarra durante un incidente.
Primero: confirma el radio de blast y qué capa está caliente
-
¿Son todas las consultas o endpoints específicos? Si es un solo endpoint, sospecha de un plan de consulta, estadísticas o
un hotspot de tabla—no del “clúster”. -
Revisa la saturación de la capa SQL: CPU de TiDB, conteo de conexiones, threadpool. En MySQL, revisar CPU del primario
y threads activos. - Revisa tasas de error/timeouts: los timeouts a menudo preceden a métricas de saturación visibles.
Segundo: comprobar latencia de almacenamiento y compactación/retropresión
- MySQL: iostat, longitud de history list de InnoDB, hit rate del buffer pool, latencia de fsync.
- TiDB/TiKV: bytes pendientes de compactación de RocksDB, write stalls, duración de apply en raftstore, utilización de disco del store.
Tercero: comprobar patologías de distribución
- Regiones calientes: una sola región dominando lecturas/escrituras.
- Desbalance de líderes: un store tiene demasiados líderes.
- Red: retransmisiones, pérdida de paquetes o sorpresas de latencia entre AZs.
- Estadísticas y regresiones de plan: cambios repentinos de plan después de deploy/DDL/analyze.
Si haces estas comprobaciones en orden, normalmente encuentras el cuello de botella rápido. Si las haces fuera de orden, puedes
pasar horas afinando SQL mientras un nodo de almacenamiento se está autoestallando.
Tres mini-historias corporativas desde el terreno
Mini-historia 1: El incidente causado por una suposición equivocada
Una empresa SaaS mediana migró un servicio relacionado con pagos de MySQL a TiDB. La aplicación usaba el protocolo MySQL y pasó
tests de integración. Todos celebraron. Programaron el corte para un martes porque el martes “no es un día ocupado”, cosa que la gente
dice justo antes de que el martes se vuelva ocupado.
La suposición equivocada fue simple: asumieron que su camino de lectura era seguro con semánticas REPEATABLE READ y transacciones de larga duración.
En MySQL, su patrón era “abrir transacción, leer unas cosas, llamar a un par de servicios internos y luego escribir.” Era lento, pero soportable
en un primario y un par de réplicas.
En TiDB, el mismo patrón creó una acumulación de locks y conflictos durante un pico de tráfico. La coordinación de transacciones distribuidas amplificó
lo que antes era contención leve en p99 de latencia y reintentos por timeout, los cuales empeoraron la contención. Una tormenta de reintentos es
una profecía autocumplida con mejor logging.
La solución no fue exótica. Acortaron el scope de las transacciones, eliminaron llamadas de red dentro de transacciones DB y hicieron la lógica
“leer y luego escribir” idempotente con constraints únicos apropiados. Después de eso, TiDB se comportó bien. La compatibilidad nunca fue el problema;
fueron sus hábitos transaccionales.
Mini-historia 2: La optimización que se volvió en contra
Otra empresa corría MySQL en NVMe de alta gama y en una revisión de rendimiento alguien sugirió “añadamos más índices para acelerar lecturas.” Añadieron
varios índices compuestos en una tabla grande de eventos. Las lecturas mejoraron en un dashboard. Todos asintieron. Entonces el path de escritura empezó a
degradarse.
El contragolpe fue clásico: la canalización de ingestión era intensiva en escrituras y cada índice convirtió cada insert en amplificación adicional de escritura.
InnoDB empezó a gastar más tiempo manteniendo índices secundarios que en hacer el trabajo real. El binlog creció, el lag de replicación aumentó y el equipo empezó
a enrutar más lecturas al primario “temporalmente”, lo que volvió al primario aún más ocupado. Temporalmente es cómo los outages se convierten en hábito.
Intentaron “resolverlo” moviéndose a TiDB, esperando que el escalado horizontal borrara el problema. No lo hizo. TiDB distribuyó la amplificación de escritura entre
nodos TiKV, pero también aumentó la presión de compactación. El clúster se mantuvo arriba, pero la latencia tail empeoró durante picos de ingest.
La solución final fue aburrida: eliminar índices de poco valor, introducir tablas rollup para consultas de dashboard y mover algunas cargas analíticas a TiFlash.
La lección no fue “los índices son malos.” Fue: optimizar sin entender tu camino de escritura es sabotaje con buenas intenciones.
Mini-historia 3: La práctica aburrida pero correcta que salvó el día
Una tercera organización corría TiDB para una carga de cara al cliente y MySQL para varias herramientas internas. Su clúster TiDB tenía una práctica estricta:
game days trimestrales y simulacros de restauración mensuales. No “restauramos una vez en staging”, sino “podemos restaurar una snapshot consistente y levantar
la app contra ella.”
Una mañana, un deploy introdujo un bug sutil: un job en background escribió datos incorrectos en una tabla crítica. El bug no fue obvio de inmediato. Pasó tests
unitarios y solo se manifestó bajo ciertas distribuciones de datos. Cuando lo detectaron, las escrituras incorrectas llevaban horas.
Porque habían practicado, el equipo no mutó producción en pánico. Pararon el job, tomaron una snapshot de incidente para forense y restauraron a un punto en el tiempo
conocido bueno en un clúster paralelo. Luego reprodujeron un subconjunto limpiado de escrituras correctas. El servicio se mantuvo parcialmente disponible y el impacto
en clientes fue limitado.
Nadie recibió aplausos por “corrimos simulacros de restauración.” Pero funcionó porque fue aburrido. La confiabilidad raramente es ingeniosa. Es mayormente ensayo
y negarse a saltarse los pasos molestos.
Errores comunes: síntoma → causa raíz → solución
1) Síntoma: “TiDB está lento” solo para un inquilino o un user_id
Causa raíz: hotspotting en un rango de clave estrecho (una sola Región, un solo líder) debido a IDs monotonamente crecientes o patrones de acceso por tenant.
Solución: rediseñar claves (prefijo hash, scatter), habilitar estrategia de split de regiones, añadir caching o separar tenants calientes. Confirmar con salidas de PD hot region.
2) Síntoma: p99 con picos de latencia durante escrituras pico; CPU parece bien
Causa raíz: backlog de compactación de RocksDB en TiKV y write stalls; el almacenamiento no puede seguir el ritmo de la amplificación de escritura.
Solución: aumentar throughput de disco, añadir nodos TiKV, reducir número de índices, agrupar escrituras, revisar settings de compactación y opciones de compresión.
3) Síntoma: el primario MySQL parece sano pero las réplicas se atrasan impredeciblemente
Causa raíz: transacciones grandes, ráfagas de escritura intensas o réplicas IO bound; a veces el apply SQL single-threaded es el limitador.
Solución: fragmentar transacciones, afinar paralelismo de replicación, reducir churn del binlog (índices, formato de fila) y validar latencia de almacenamiento en réplicas.
4) Síntoma: aumentan deadlocks o timeouts de lock tras mover a TiDB
Causa raíz: transacciones largas y filas con alta contención se vuelven más costosas en rutas de commit distribuidas; los reintentos amplifican la carga.
Solución: acortar transacciones, evitar patrones “select luego update”, usar patrones de concurrencia optimista y hacer los reintentos con jitter y límites.
5) Síntoma: un cambio de esquema causa latencia en todo el clúster y IO elevado
Causa raíz: el DDL en línea aún genera trabajo de fondo (backfill/reorg) y aumenta carga de lectura/escritura en TiKV.
Solución: programar DDL en tráfico bajo, aplicar throttling si está disponible, asegurar margen de capacidad y vigilar métricas de compactación/apply de raft durante el DDL.
6) Síntoma: después de la migración, algunas consultas devuelven resultados distintos
Causa raíz: dependencia de comportamientos específicos de MySQL: casts implícitos, diferencias de intercalación, GROUP BY no determinista o ordering indefinido sin ORDER BY.
Solución: hacer el SQL explícito: ORDER BY correcto, modos SQL estrictos donde sea posible, casts explícitos, verificar collations y añadir tests para consultas en casos límite.
7) Síntoma: clúster TiDB “sano” pero throughput cae tras la pérdida de un nodo
Causa raíz: reelección de líderes y reequilibrio cambiaron la distribución de líderes de regiones; los stores sobrevivientes quedan sobrecargados.
Solución: asegurar margen de capacidad, usar reglas de colocación alineadas con dominios de fallo y reequilibrar líderes. Validar con salidas de PD store y hot region.
8) Síntoma: MySQL “está bien” hasta que corren los backups, luego todo va lento
Causa raíz: contención de IO por backups y overhead de snapshots; la herramienta de backup compite con IO y cache de producción.
Solución: ejecutar backups desde réplicas, limitar IO de backups, programar fuera de pico, aislar tráfico/almacenamiento de backups y verificar capacidad de restauración regularmente.
Listas de verificación / plan paso a paso
Checklist A: Cuando debes quedarte en MySQL (y dormir mejor)
- Tu primario cabe en una sola máquina potente y la QPS de escritura no explota.
- Puedes usar réplicas, caching y tuning de queries/índices para cumplir SLOs.
- No necesitas consistencia fuerte entre múltiples primarios.
- Tu equipo no está preparado para operar una plataforma de base de datos distribuida 24/7.
- Puedes aceptar obsolescencia de réplicas para lecturas no críticas.
Checklist B: Cuando TiDB es un movimiento racional (no para lucir en el CV)
- Ya estás shardando MySQL o a punto de hacerlo, y eso se está convirtiendo en un riesgo de producto.
- Necesitas escalado horizontal con semántica transaccional.
- Puedes comprometerte con observabilidad, formación on-call y disciplina de upgrades.
- Puedes provisionar suficientes nodos para mantener margen (cómputo y almacenamiento).
- Tienes un plan para hotspots, no solo una esperanza.
Plan de migración paso a paso (versión pragmática)
- Inventario de “MySQL-ismos” en tu app: modos SQL, casts implícitos, dependencia en ordering indefinido, funciones no estándar.
- Línea base de rendimiento: principales consultas por latencia y por tiempo total; tamaños de transacción; pico de throughput de escrituras.
- Diseñar para la contención: identificar hot rows, contadores y patrones de “última actualización”; rediseñar antes de migrar.
- Provisionar TiDB con margen: no arrancar con “lo justo.” Los sistemas distribuidos castigan márgenes ajustados.
- Decidir colocación y dominios de fallo: réplicas entre AZs/racks; quorum de PD; conteo de TiKV.
- Probar carga con datos y skew similares a producción: carga sintética uniforme es cómo fallas en detectar hotspots.
- Ejecutar lecturas duales con cuidado: comparar resultados para consultas críticas; vigilar diferencias de collation/cast.
- Planear corte con rollback: bien sea doble escritura con verificación o una ventana de freeze controlada.
- Hacer simulacros de restauración antes del go-live: backup sin restaurar es teatro.
- Formar al on-call: “qué es un hotspot de región” no debe descubrirse durante un incidente.
- Después del corte, congelar DDL y cambios de tuning: observar comportamiento estable antes de “optimizar”.
- Configurar alertas impulsadas por SLO: latencia y tasas de error primero, luego señales de saturación (CPU, compactación, balance de líderes).
Segundo y último chiste: Una base de datos distribuida es igual que una base de datos normal, excepto que puede fallar en más lugares a la vez.
Preguntas frecuentes
1) ¿TiDB es realmente “drop-in” para aplicaciones MySQL?
Drop-in para el protocolo y muchos patrones SQL, sí. Drop-in para el comportamiento, no. Asume que encontrarás casos límite:
collations, casts implícitos, patrones de alcance transaccional y diferencias de rendimiento bajo cargas sesgadas.
2) Si hoy no estoy shardando, ¿debo moverme a TiDB “por si acaso”?
Normalmente no. Si MySQL cumple tus SLOs con prácticas operativas sensatas, quédate. TiDB es un movimiento estratégico cuando el sharding
o la necesidad de múltiples primarios se vuelven un riesgo de producto inevitable.
3) ¿TiDB arreglará automáticamente mis consultas lentas?
Puede ayudar si tu cuello de botella es la capacidad de un solo nodo y la consulta se paraleliza bien. No arreglará índices faltantes, malos patrones de consulta
o transacciones enormes. De hecho, la ejecución distribuida puede hacer algunas consultas malas más costosas.
4) ¿Cuál es el modo de fallo de rendimiento más común de TiDB en la vida real?
Hotspots y retropresión del almacenamiento. Los hotspots concentran carga en pocas regiones/stores. La retropresión aparece como backlog de compactación
de RocksDB y write stalls, que luego se traducen en latencia de consultas.
5) ¿Cuál es el modo de fallo de rendimiento más común de MySQL en la vida real?
Contención de locks y stalls de IO, a menudo desencadenados por una consulta “inocente” o una migración ejecutada en el momento equivocado.
El lag de replicación es el subcampeón que rompe silenciosamente suposiciones de corrección.
6) ¿Cómo difieren los backups operativamente?
Los backups de MySQL son conceptualmente más simples: un dataset primario, más réplicas. Los backups de TiDB deben capturar consistencia distribuida
a través de stores. En ambos casos, la única prueba significativa es restaurar y ejecutar la aplicación contra ella.
7) ¿Puede TiDB reemplazar réplicas de lectura y separación lectura/escritura?
TiDB puede escalar lecturas añadiendo nodos TiDB, y también puede usar followers en algunas configuraciones. Pero aún necesitas pensar dónde aterriza la carga:
CPU de la capa SQL, hotspots en TiKV y red. No es “sin separación”, es una separación diferente.
8) ¿Los cambios de esquema son más fáciles en TiDB?
A menudo son más fáciles desde el punto de vista de disponibilidad de la aplicación, porque muchas operaciones DDL son en línea. Pero todavía generan trabajo de fondo
que puede degradar el rendimiento si no tienes margen y buena observabilidad.
9) ¿Necesito un equipo de plataforma dedicado para correr TiDB?
No necesitas un equipo enorme, pero sí propiedad clara, competencia on-call y una cultura de ensayos. Si tu organización trata bases de datos como mascotas, TiDB
se convertirá en una mascota cara con opiniones.
10) ¿Cuál es un enfoque piloto razonable?
Empieza con un servicio que tenga patrones de acceso claros, buena cobertura de pruebas y SQL no “ingenioso.” Duplicar tráfico para lecturas, validar resultados,
luego mover progresivamente rutas de escritura con un plan de rollback.
Conclusión: pasos prácticos siguientes
Si recuerdas una cosa: MySQL y TiDB son ambas bases de datos serias. La diferencia es dónde vive la complejidad y cómo falla bajo estrés.
-
Si estás en MySQL: haz primero el trabajo poco glamuroso—higiene de índices, disciplina de alcance de transacciones,
monitorización del lag de réplicas y restores probados. Puede que compres años de pista sin migrar. -
Si evalúas TiDB: ejecuta un piloto con carga moldeada por la carga real. Valida no solo la corrección funcional
sino la latencia tail bajo skew, comportamiento de hotspots y qué ocurre durante pérdida de nodos y upgrades rolling. -
Si ya estás migrando: asume que existen brechas de compatibilidad y búsquelas deliberadamente. Elimina transacciones largas,
haz el SQL explícito y construye un playbook de incidentes antes de que la producción te haga uno.
Elige el sistema cuyos modos de fallo puedas diagnosticar rápidamente, cuyo trabajo operativo puedas dotar de personal honestamente y cuya historia “simple”
se mantenga en la carga máxima. Esa es la elección adulta.