Tu equipo de producto dice “los informes son lentos”, y lo que realmente quieren decir es: los paneles caducan, las exportaciones CSV tardan minutos,
y el primario de MariaDB parece que está minando criptomonedas. Mientras tanto, la latencia del checkout aumenta y empiezas
a recibir ese tipo especial de alarmas que arruinan los fines de semana.
Esto no es un misterio. Estás intentando hacer analítica en un motor OLTP, con restricciones OLTP, y modelos de datos OLTP,
mientras finges que está bien porque “es solo SQL”. No está bien. Es caro, riesgoso y, por lo general, evitable.
Por qué los “informes” matan a MariaDB (y por qué los índices no son una religión)
MariaDB (y MySQL) son fantásticos para cargas transaccionales: lecturas y escrituras pequeñas, índices ajustados,
rutas de acceso predecibles y patrones de concurrencia moldeados por las solicitudes de usuarios. Tu aplicación realiza muchas
“obtener una orden”, “insertar un pago”, “actualizar una sesión”. El motor está diseñado para que eso sea rápido y seguro.
Los informes son distintos. Los informes hacen escaneos amplios, joins grandes, agregaciones agrupadas y patrones tipo ventana
que procesan millones de filas para resumir “todo”. Además aparecen en el peor momento posible:
fin de mes, fin de trimestre, lunes por la mañana, justo cuando el tráfico se dispara.
OLTP vs analítica: no es solo “consultas más grandes”
El rendimiento OLTP se trata de latencia y contención. El rendimiento analítico se trata de rendimiento y ancho de banda:
qué tan rápido puedes escanear, decodificar, filtrar y agregar datos. Son físicas diferentes.
- Almacenamiento por filas vs por columnas: MariaDB almacena filas. La analítica quiere columnas. Escanear 2 columnas de 50 no debería obligarte a leer 50.
- Dependencia de índices: OLTP se apoya en índices. La analítica con frecuencia prefiere escaneos con ejecución vectorizada y almacenamiento columnar comprimido.
- Patrones de concurrencia: OLTP son muchas consultas cortas; la analítica puede ser pocas consultas largas que ocupan CPU, memoria e IO.
- Realidad del modelo de datos: los esquemas normalizados son limpios para escrituras; los esquemas desnormalizados son más rápidos para agregados y joins en tiempo de consulta.
El daño real: los informes no solo van lentos, desestabilizan la base de datos
“Lento” es molesto. “Lento y desestabilizador” es un incidente. Una consulta de informe puede:
- Desalojar tu conjunto de trabajo OLTP caliente del buffer pool.
- Inundar el disco con lecturas (y escrituras de undo/redo si tablas temporales derraman).
- Crear retraso de replicación (las escrituras siguen ocurriendo, pero la réplica no puede aplicarlas a tiempo).
- Disparar bloqueos de metadatos de forma incómoda durante cambios de esquema.
- Causar saturación de CPU que hace que todas las consultas sean lentas, incluidas las que importan.
Si has vivido con “añade un índice” como mantra, aquí está la trampa: los índices aceleran consultas selectivas.
Los informes a menudo no son selectivos. O son selectivos en un campo de baja cardinalidad que hace que los índices sean menos útiles.
O hacen joins de tablas grandes de formas que hacen imposible elegir el “índice correcto” porque no existe un índice correcto.
Broma #1: Añadir índices para arreglar reporting es como añadir más carriles a una autopista para arreglar la hora punta—felicidades, ahora tienes un embotellamiento más ancho.
Datos interesantes y un poco de historia (para que dejes de discutir con la gravedad)
- MariaDB se bifurcó de MySQL tras preocupaciones por la adquisición de Sun por Oracle; mantuvo compatibilidad con MySQL mientras divergía en motores de almacenamiento y características.
- ClickHouse se originó en Yandex para analítica a escala web; su diseño asume que escaneos enormes y agregados rápidos son la norma, no la excepción.
- El almacenamiento columnar se volvió dominante en analítica porque leer menos bytes importa más que “tener el índice correcto” una vez que los conjuntos de datos crecen.
- La compresión es una característica en los almacenes columnares: mejor compresión significa menos IO, y menos IO significa más velocidad. No se trata solo de ahorrar disco.
- MergeTree (la familia principal de motores de ClickHouse) se basa en merges en segundo plano y partes inmutables—genial para ingestión + lecturas, con modos de fallo distintos a los B-trees.
- Las materialized views en ClickHouse se usan a menudo como “preagregaciones”, un patrón popularizado por sistemas OLAP para intercambiar almacenamiento por latencia predecible.
- Los esquemas estrella y tablas de hechos desnormalizadas se volvieron comunes porque hacer joins de tablas transaccionales normalizadas a escala analítica es costoso y frágil.
- El retraso de replicación como síntoma es anterior a los stacks analíticos modernos: “ejecutar informes en una réplica” ha sido un parche desde los primeros días de MySQL, y aún duele.
En qué ClickHouse es bueno (y en qué no lo es)
ClickHouse es una base de datos OLAP diseñada para lecturas rápidas sobre grandes conjuntos de datos. Lee datos columnarios,
ejecuta consultas de forma vectorizada, comprime agresivamente y puede escalar horizontalmente.
Está pensado para “dame agregados sobre un mes de eventos”, no para “actualizar una fila por clave primaria 5.000 veces por segundo”.
Fortalezas de ClickHouse
- Escaneos y agregados rápidos: group-bys, intervalos de tiempo, cálculos tipo percentil, listas top-N.
- Alta compresión: a menudo obtienes huellas de almacenamiento sorprendentemente pequeñas para datos tipo evento.
- Ingesta amigable para append: insertar por lotes; deja que los merges los ordenen después.
- Opciones de pre-aggregación: las materialized views pueden construir tablas resumen a medida que llegan los datos.
- Ejecución distribuida: escala lecturas y almacenamiento entre shards/replicas con el diseño adecuado.
Debilidades de ClickHouse (aka dónde la gente se hace daño)
- Actualizaciones/eliminaciones no son OLTP: sí, puedes hacer mutaciones, pero son pesadas y no son el camino feliz.
- Los joins pueden ser caros si lo tratas como un esquema OLTP normalizado y esperas milagros.
- Patrones de consistencia eventual: diseñarás para datos tardíos, deduplicación e idempotencia.
- Matices operativos: merges, partes, thresholds de disco y límites de memoria no son conocimiento opcional.
Aquí va la recomendación central: mantén MariaDB para transacciones y estado como fuente de verdad.
Usa ClickHouse para reporting, paneles y exploración. No les hagas pelear por los mismos recursos.
Marco de decisión: mantener MariaDB, optimizar o delegar
No todo informe lento requiere una base de datos nueva. Algunos requieren una mejor consulta, una tabla resumen,
o admitir que el panel no necesita frescura segundo a segundo. Pero una vez que la carga es fundamentalmente analítica,
MariaDB seguirá cobrando intereses en forma de cajas más grandes, más réplicas y más incidentes.
Cuándo quedarse en MariaDB (por ahora)
- El conjunto de datos es pequeño (cabe cómodamente en memoria) y las consultas son simples.
- Los informes pueden precomputarse cada noche sin impacto para el negocio.
- La mayor parte de la lentitud proviene de errores de consulta evidentes (predicados faltantes, joins N+1 en la capa de informes, cross joins accidentales).
- No tienes capacidad operativa para ejecutar otro sistema todavía.
Cuándo delegar a ClickHouse
- Los informes escanean millones de filas rutinariamente y aún requieren latencia interactiva.
- Los paneles están activos constantemente y compiten con el tráfico de producción.
- Necesitas capacidad de segmentación flexible sobre datos tipo evento (series temporales, logs, clicks, órdenes, movimientos de inventario).
- “Ejecutarlo en una réplica” está causando retraso de replicación o la réplica se volvió crítica para producción de todos modos.
Cuándo replantear el producto en su lugar
A veces la solución honesta no es técnica. Es ajustar expectativas.
Si finanzas quiere “análisis de cohortes de todo el tiempo” bajo demanda sobre cinco años de datos, la respuesta es:
construyan la canalización y almacenamiento adecuados y acepten que cuesta dinero y tiempo.
Guía rápida de diagnóstico
Quieres el cuello de botella en minutos, no después de una semana de ajustes por intuición. Aquí está el orden que funciona en la vida real.
Primero: ¿MariaDB se está ahogando por CPU, IO, locks o memoria?
- CPU al máximo: planes malos, joins grandes, sin selectividad, ordenamientos/agregaciones pesadas.
- Limitado por IO: misses en el buffer pool, escaneos de tabla, derrames a tablas temporales, mala localidad, demasiadas lecturas aleatorias.
- Limitado por locks: locks de metadatos, transacciones largas o informes sosteniendo locks más tiempo del esperado.
- Presión de memoria: ordenamientos, tablas temporales o buffer pool pequeño causando churn.
Segundo: ¿es el desajuste de carga el verdadero problema?
- Las consultas requieren escanear “la mayor parte de la tabla” incluso con índices.
- El negocio pide group-bys de alta cardinalidad y filtros flexibles.
- Los informes son frecuentes y se ejecutan en horas pico.
Tercero: ¿es la canalización de datos tu problema oculto?
- El retraso de la réplica hace inútil “ejecutar informes en la réplica”.
- CDC o ETL introducen duplicados o eventos fuera de orden.
- Los cambios de esquema rompen la canalización y lo descubres una semana después.
Cuarto: confirma midiendo una consulta representativa de extremo a extremo
Elige una consulta que duela. Obtén su plan en MariaDB, ejecútala con tracing y cuantifica filas examinadas, tablas temporales,
pasadas de ordenamiento y tiempo de espera. Luego decide: optimizar en MariaDB o dejar de torturarla y delegar.
Una cita para mantener la honestidad: “La esperanza no es una estrategia.” — Gene Kranz
Tareas prácticas: comandos, salidas y decisiones (12+)
Estos no son comandos “de juguete”. Son las jugadas del día a día que usas para decidir el siguiente paso.
Para cada tarea: ejecuta el comando, interpreta la salida y entonces toma una decisión.
Tarea 1: Encontrar las consultas más lentas en MariaDB (slow query log)
cr0x@server:~$ sudo pt-query-digest /var/log/mysql/mariadb-slow.log | head -n 30
# 120s user time, 3s system time, 1.23M rss, 2.45G vsz
# Current date: Mon Dec 30 10:12:44 2025
# Hostname: db-primary
# Files: mariadb-slow.log
# Overall: 8.42k total, 97 unique, 0.23 QPS, 0.01x concurrency
# Time range: 2025-12-29T09:00:00 to 2025-12-30T09:00:00
# Attribute total min max avg 95% stddev median
# ============ ======= ======= ======= ======= ======= ======= =======
# Exec time 13200s 100ms 95s 1.6s 12s 4.3s 450ms
# Rows examine 2.1e11 0 9.2e8 2.5e7 1.1e8 6.3e7 8.2e6
# Query 1: 38% RPS, 61% time, 80% rows examined
Qué significa: Las filas examinadas son enormes en relación a las filas devueltas. Eso es clásico “consulta analítica sobre tablas OLTP.”
Decisión: Si los peores culpables son agrupaciones/agregaciones sobre rangos temporales grandes, planifica una delegación.
Si es una consulta con un predicado faltante, arregla la consulta primero.
Tarea 2: Comprobar los threads actuales de MariaDB y si están bloqueados
cr0x@server:~$ mariadb -e "SHOW PROCESSLIST\G" | sed -n '1,60p'
*************************** 1. row ***************************
Id: 8123
User: report_user
Host: 10.2.4.19:52311
db: app
Command: Query
Time: 184
State: Sending data
Info: SELECT customer_id, count(*) FROM orders WHERE created_at >= '2025-10-01' GROUP BY customer_id
*************************** 2. row ***************************
Id: 8130
User: app_user
Host: 10.2.7.11:49821
db: app
Command: Query
Time: 2
State: Updating
Info: UPDATE inventory SET qty=qty-1 WHERE sku='X'
Qué significa: “Sending data” durante cientos de segundos suele ser escaneo/agregación, no transferencia de red.
Decisión: Si los threads de informes son de larga duración y numerosos, limítalos, muévelos a un sistema separado o ambos.
Tarea 3: Confirmar si el plan de la consulta está haciendo un escaneo
cr0x@server:~$ mariadb -e "EXPLAIN SELECT customer_id, count(*) FROM orders WHERE created_at >= '2025-10-01' GROUP BY customer_id\G"
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: orders
type: range
possible_keys: idx_orders_created_at
key: idx_orders_created_at
rows: 48219321
Extra: Using index condition; Using temporary; Using filesort
Qué significa: Incluso con un índice de rango, estás escaneando ~48M de filas, luego temp + filesort para el group-by.
Decisión: Si “rows” está en decenas de millones y el informe es frecuente, deja de optimizar en los bordes. Delegar.
Tarea 4: Comprobar el comportamiento del buffer pool de InnoDB (¿está thrashing?)
cr0x@server:~$ mariadb -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+------------+
| Variable_name | Value |
+---------------------------------------+------------+
| Innodb_buffer_pool_read_requests | 9923412331 |
| Innodb_buffer_pool_reads | 183492211 |
+---------------------------------------+------------+
Qué significa: La proporción indica que muchas lecturas se satisfacen desde memoria, pero 183M de lecturas físicas sigue siendo enorme.
Observa la tendencia durante las ventanas de reporting.
Decisión: Si las lecturas del buffer pool se disparan cuando se ejecutan informes y la latencia de la app sigue, aisla la carga analítica.
Tarea 5: Comprobar derrames a tablas temporales (un asesino silencioso)
cr0x@server:~$ mariadb -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Created_tmp_disk_tables | 421193 |
| Created_tmp_tables | 702881 |
+-------------------------+----------+
Qué significa: Las tablas temporales en disco son caras: IO extra, latencia extra, más contención.
Decisión: Si las tablas temporales en disco crecen rápidamente durante los informes, o rediseña las consultas o deja de ejecutarlas en MariaDB.
Tarea 6: Medir el retraso de replicación si ejecutas informes en réplicas
cr0x@server:~$ mariadb -h db-replica -e "SHOW SLAVE STATUS\G" | egrep 'Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running'
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 1870
Qué significa: 31 minutos de retraso. Tu “réplica de reporting” es ahora una máquina del tiempo.
Decisión: Deja de tratar a las réplicas como motores analíticos. Delegar a ClickHouse o construir réplicas analíticas dedicadas con garantías distintas.
Tarea 7: Identificar si el host está limitado por IO (Linux)
cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (db-primary) 12/30/2025
avg-cpu: %user %nice %system %iowait %steal %idle
52.21 0.00 6.12 18.44 0.00 23.23
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s w_await %util
nvme0n1 2100.0 268800.0 0.0 0.00 8.10 128.0 980.0 105600.0 12.40 98.70
Qué significa: %util cerca del 99% y alto iowait: el almacenamiento está saturado. Los informes están leyendo demasiado.
Decisión: Puedes escalar el almacenamiento, pero eso compra tiempo. Mejor: reduce el volumen de escaneo moviendo la analítica a almacenamiento columnar.
Tarea 8: Comprobar tamaños de tablas y crecimiento en MariaDB (presión de capacidad)
cr0x@server:~$ mariadb -e "SELECT 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 10;"
+-------------------+-------+
| table_name | gb |
+-------------------+-------+
| events | 612.45 |
| orders | 189.12 |
| order_items | 141.77 |
| sessions | 88.04 |
+-------------------+-------+
Qué significa: Tablas grandes con uso mixto OLTP + analítica (como events) son candidatas principales para delegar.
Decisión: Empieza con las tablas más grandes que generan escaneos; mantén las tablas transaccionales en MariaDB.
Tarea 9: Línea base de salud del servidor ClickHouse
cr0x@server:~$ clickhouse-client -q "SELECT version(), uptime(), round(memory_usage/1024/1024) AS mem_mb FROM system.metrics WHERE metric IN ('MemoryTracking')"
24.9.2.42
86400
512
Qué significa: Puedes rastrear MemoryTracking y uptime rápidamente; si esto ya está alto en reposo, tienes un problema de carga o configuración.
Decisión: Establece límites de memoria sensatos e investiga consultas descontroladas antes de incorporar paneles.
Tarea 10: Comprobar partes/merges en ClickHouse (¿te estás ahogando en partes pequeñas?)
cr0x@server:~$ clickhouse-client -q "SELECT table, count() AS parts, sum(rows) AS rows, round(sum(bytes_on_disk)/1024/1024/1024,2) AS gb FROM system.parts WHERE active GROUP BY table ORDER BY parts DESC LIMIT 5"
events 18234 891233112 122.31
orders 1820 83211299 18.44
Qué significa: 18k partes para events sugiere inserciones demasiado pequeñas o particionado deficiente.
Decisión: Agrupa las inserciones, afina el particionado y garantiza que los merges puedan seguir el ritmo; de lo contrario la latencia de consulta será irregular.
Tarea 11: Encontrar consultas lentas en ClickHouse (system.query_log)
cr0x@server:~$ clickhouse-client -q "SELECT query_duration_ms, read_rows, read_bytes, memory_usage, query FROM system.query_log WHERE type='QueryFinish' AND event_time > now()-3600 ORDER BY query_duration_ms DESC LIMIT 5"
12034 981233112 44120341212 2147483648 SELECT customer_id, count() FROM events WHERE ts > now()-86400 GROUP BY customer_id
Qué significa: Read rows/bytes te dicen si la consulta está escaneando demasiado; memory usage sugiere gran estado de group-by.
Decisión: Si las lecturas son enormes, revisa primary key/order by y particionado; si la memoria es enorme, considera preagregación.
Tarea 12: Verificar límites de disco y espacio libre en nodos ClickHouse
cr0x@server:~$ df -h /var/lib/clickhouse
Filesystem Size Used Avail Use% Mounted on
/dev/md0 3.6T 3.1T 420G 89% /var/lib/clickhouse
Qué significa: ClickHouse necesita margen para merges; estar cerca del lleno hace los merges lentos y los fallos extraños.
Decisión: Mantén espacio libre significativo; si estás consistentemente por encima de ~80–85%, planifica capacidad o políticas de ciclo de vida ahora.
Tarea 13: Verificar retraso de CDC (seguimiento de posición del binlog vía conector)
cr0x@server:~$ sudo journalctl -u mariadb-to-clickhouse-cdc --since "10 min ago" | tail -n 8
Dec 30 10:01:11 cdc1 cdc[1842]: last_binlog=mysql-bin.003211:918233112
Dec 30 10:01:21 cdc1 cdc[1842]: last_binlog=mysql-bin.003211:918244901
Dec 30 10:01:31 cdc1 cdc[1842]: lag_seconds=3
Qué significa: Lag bajo y estable es lo que quieres. Los picos significan presión en downstream o problemas del conector.
Decisión: Si el lag se dispara durante ventanas de informes, tu delegación está incompleta: o los paneles siguen golpeando MariaDB o ClickHouse no puede ingerir lo suficientemente rápido.
Tarea 14: Confirmar que ClickHouse realmente usa el índice (data skipping) vía EXPLAIN
cr0x@server:~$ clickhouse-client -q "EXPLAIN indexes=1 SELECT count() FROM events WHERE ts >= now()-3600 AND customer_id=12345"
Indexes:
PrimaryKey
Keys:
ts
customer_id
Condition: (ts >= (now() - 3600)) AND (customer_id = 12345)
Qué significa: Quieres condiciones que alineen con el ORDER BY para que ClickHouse pueda saltar gránulos.
Decisión: Si tus filtros comunes no aparecen en la primary key, tu esquema está peleando con tus consultas.
Diseñar la delegación: flujo de datos, latencia y corrección
“Delegar analítica” suena limpio hasta que preguntas las preguntas molestas: ¿Qué tan fresca debe ser la data?
¿Qué pasa cuando se actualiza una fila? ¿Cómo modelas los borrados? ¿Cuál es la fuente de verdad?
Elige tu patrón de delegación
- Batch ETL: exportaciones nocturnas/por hora desde MariaDB a ClickHouse. Simple, barato, no en tiempo real.
- CDC (captura de cambios): transmitir cambios del binlog a ClickHouse continuamente. Más piezas móviles, menor latencia.
- Dual-write: la aplicación escribe tanto en MariaDB como en ClickHouse. Camino rápido hacia el arrepentimiento a menos que tengas mucha disciplina.
Para la mayoría de las empresas: Batch ETL para reporting de bajo riesgo, CDC para paneles que mueven operaciones, y evita dual-write a menos que disfrutes depurar consistencia a las 2 a.m.
Define “corrección” como adulto
La corrección analítica rara vez es “coincide exactamente con OLTP en cada milisegundo.” Usualmente es:
- Los datos están completos hasta T menos N minutos.
- Se manejan eventos tardíos (ventana de backfill).
- La deduplicación es determinista.
- Las métricas de negocio se definen una vez, no se reinventan por panel.
Cómo manejar actualizaciones y borrados desde MariaDB
En MariaDB, una actualización de fila es un día normal. En ClickHouse, típicamente modelas el cambio a lo largo del tiempo:
- Eventos inmutables: hechos append-only (orden creado, pago liquidado). Mejor caso.
- ReplacingMergeTree: mantener la versión más reciente mediante una columna de versión; los duplicados se resuelven eventualmente por merges.
- CollapsingMergeTree: modelar signo (+1/-1) para semántica de “insert/delete”, útil para ciertos streams de eventos.
- Mutations: pesadas; úsalas con moderación para correcciones reales, no para actualizaciones rutinarias.
Si tu tabla OLTP es “estado actual” (como inventario actual), puedes replicarla en ClickHouse,
pero no esperes que se comporte como una tabla actualizada transaccionalmente. Para analítica, las tablas de estado a menudo se derivan de eventos.
Modelado de esquema que no envejece mal
La forma más rápida de hacer que ClickHouse sea lento es importar tu esquema normalizado de MariaDB tal cual y luego sorprenderte.
La segunda forma más rápida es elegir un ORDER BY que no coincida con tus filtros reales.
Empieza con las preguntas, no con las tablas
Enumera tus 10 consultas principales de panel. Reales, no aspiracionales. Para cada una, captura:
- Filtros de rango temporal (última hora, último día, rango de fechas arbitrario)
- Dimensiones de alta selectividad (customer_id, org_id, tenant_id)
- Dimensiones de group-by (country, plan, product_id)
- Cardinalidad esperada (¿cuántos grupos?)
- Frecuencia de actualización y SLO
Estrategia de clave de orden: el superpoder de “saltar datos”
La primary key de ClickHouse (la expresión ORDER BY en tablas MergeTree) no es un índice B-tree.
Es cómo se ordenan físicamente los datos, permitiendo saltar bloques cuando los predicados coinciden con el orden.
Si cada panel filtra por tiempo y tenant, tu clave de orden a menudo empieza con esos:
(tenant_id, ts) o (ts, tenant_id). Cuál va primero depende de los patrones de acceso.
- Mayormente “un tenant, muchos rangos temporales”: (tenant_id, ts)
- Mayormente “cortes globales por tiempo sobre todos los tenants”: (ts, tenant_id)
Particionado: mantenlo aburrido
El particionado ayuda a gestionar el ciclo de vida de los datos y el ámbito de los merges. Sobre-particionar crea demasiadas partes y dolor operativo.
Un patrón común: particionar por mes para datos de eventos, por día para volumen extremadamente alto, o por tenant solo si los tenants son enormes y están aislados.
Materialized views: preagrega para paneles predecibles
La queja “el panel es lento” suele ser “group-by sobre 90 días es lento.” Preagrega las métricas calientes.
Construye tablas resumen indexadas por las dimensiones que realmente filtras. Entonces los paneles consultan resúmenes, no hechos crudos.
Broma #2: Las materialized views son como preparar comidas—pasas un poco de tiempo al principio para no llorar con comida para llevar a las 11 p.m.
Operaciones y fiabilidad: ejecutar ClickHouse en serio
Delegar analítica no es “instalar ClickHouse y relajarse”. Estás introduciendo un nuevo sistema en producción.
Necesita monitorización, backups, planificación de capacidad, disciplina en cambios de esquema y respuesta a incidentes.
Qué monitorizar (higiene mínima SRE)
- Retraso de ingestión: desde binlog de MariaDB hasta la disponibilidad en la tabla ClickHouse.
- Backlog de merges: conteo de partes, merges en progreso, bytes por merge.
- Uso de disco y espacio libre: los merges necesitan margen; discos casi llenos causan fallos en cascada.
- Percentiles de latencia de consultas: para consultas de panel y exploración ad-hoc.
- Uso de memoria y OOM kills: los group-bys pueden asignar agresivamente sin límites.
- Salud de réplicas: si usas replicación, monitoriza la cola y errores de replicación.
Backups: decide qué estás protegiendo
Los datos de ClickHouse suelen ser derivados. Eso no significa que no los respaldes.
Significa que tu estrategia de backup puede ser diferente:
- Si puedes rehidratar desde logs crudos/almacenamiento de objetos, los backups son para recuperación más rápida, no supervivencia existencial.
- Si ClickHouse contiene verdad empresarial curada (tablas de métricas, datos corregidos), respáldalo como si importara—porque importa.
Aislamiento de recursos: evita que los paneles se conviertan en denial-of-service
Usa users/profiles/quotas. Establece límites de memoria por consulta y por usuario. Usa colas si es necesario.
El objetivo: una consulta pesada de un analista no debe dejar sin recursos a los paneles operativos.
Cambios de esquema: trátalos como despliegues de producción
Los cambios de esquema en ClickHouse suelen ser más sencillos que migraciones OLTP, pero aún tienen radio de impacto:
las materialized views dependen de columnas; las canalizaciones CDC dependen del esquema; los paneles dependen de semánticas.
Planea cambios con compatibilidad en mente y versiona tus transformaciones.
Tres microhistorias corporativas (con dolor incluido)
Microhistoria 1: El incidente causado por una suposición equivocada
Una empresa B2B mediana tenía una configuración familiar: MariaDB primario, una réplica etiquetada “reporting” y una herramienta BI
apuntando a esa réplica. Todos se sentían listos. Las escrituras en el primario, las lecturas en la réplica, paz mundial lograda.
La suposición equivocada fue sutil: creyeron que el retraso de replicación sería “suficientemente pequeño” para los paneles, y si crecía,
sería obvio. No lo fue. La herramienta BI cacheaba resultados, los usuarios exportaban CSVs y nadie notó que el número de “ingresos del mes actual”
a veces estaba treinta minutos desactualizado en horas pico. Los datos estaban equivocados, pero lo bastante consistentes para parecer creíbles.
Luego vino un lanzamiento de producto. El volumen de escrituras se disparó, y también los informes pesados. El lag de réplica subió y la herramienta BI se volvió más lenta.
Alguien “lo arregló” apuntando la herramienta BI al primario por “solo un día”. Ese día incluyó un escaneo de tabla de varias horas y varios
group-bys costosos. La latencia del checkout aumentó; la app reintentó; las escrituras aumentaron más; el primario empezó a saturar disco.
El incidente no fue un error catastrófico único. Fue la acumulación de una mala decisión arquitectónica: usar replicación como sistema analítico.
El postmortem tuvo el giro habitual: la réplica ya era crítica para producción porque alimentaba paneles orientados al cliente.
Así que no era “agradable de tener”. Era una dependencia sin SLOs y sin protecciones.
La solución fue aburrida y estructural: mover la analítica a ClickHouse, mantener la réplica para failover, definir expectativas de frescura y añadir alarmas de lag.
La replicación volvió a ser lo que debía ser: una herramienta de resiliencia, no un motor de reporting.
Microhistoria 2: La optimización que salió mal
Otra empresa delegó a ClickHouse y celebró. Los paneles bajaron de minutos a segundos.
Luego “optimizaron” la ingestión transmitiendo cada fila como un insert de una sola fila porque “en tiempo real” sonaba bien en reuniones.
En semanas, la latencia de consultas se volvió errática. A veces rápida, a veces terrible. El clúster ClickHouse parecía saludable hasta que no lo fue.
El conteo de partes subió. Los merges funcionaban constantemente. Los discos se ocuparon con trabajo en segundo plano. El clúster no estaba caído; simplemente estaba perpetuamente molesto.
El agravamiento fue clásico: ClickHouse es friendly para append, pero quiere lotes.
Los inserts de una sola fila crean muchas partes pequeñas, y muchas partes pequeñas generan presión de merges, y la presión de merges roba IO a las consultas.
Los usuarios vivieron esto como “los paneles son inestables”, lo cual es peor que “lentos”, porque es más difícil de confiar.
La remediación no fue tuning mágico. Agruparon inserts (por tiempo o por tamaño), redujeron la granularidad de partición,
y establecieron límites operativos: umbrales máximos de partes por partición con alertas. También aceptaron que “tiempo real”
significaba “en un minuto” para la mayoría de métricas, no “en un segundo”.
Fue una buena lección: el sistema más rápido es el que no fuerzas hacia comportamiento patológico.
Microhistoria 3: La práctica aburrida pero correcta que salvó el día
Una empresa relacionada con pagos ejecutaba tanto MariaDB como ClickHouse. Nada llamativo.
Tenían un contrato de datos escrito: un documento pequeño que describía cada tabla de métricas, los campos fuente,
la lógica de transformación, más una “ventana de frescura” y una consulta de prueba que validaba conteos de filas.
Cada cambio de esquema en MariaDB que tocaba tablas replicadas requería una revisión ligera:
¿rompe esto el CDC? ¿ClickHouse necesita una columna nueva? ¿una materialized view necesita actualizarse?
El proceso no era amado, pero era consistente. También ejecutaban un trabajo diario de reconciliación:
comparar conteos y sumas entre MariaDB y ClickHouse para una ventana temporal móvil.
Un viernes, un cambio aparentemente inocuo en la app empezó a escribir NULLs en un campo de dimensión que los paneles agrupaban.
A MariaDB no le importó. A ClickHouse no le importó. El panel, sin embargo, mostró una caída repentina en una métrica clave.
La comprobación de reconciliación lo detectó en una hora debido a que la cardinalidad del grupo cambió bruscamente.
El on-call no tuvo que adivinar. Revirtieron el cambio de la app, rellenaron la dimensión faltante en ClickHouse a partir de eventos fuente,
y el panel ejecutivo del lunes por la mañana no se convirtió en un drama judicial.
Nadie recibió un trofeo por ese proceso. Por eso funcionó.
Errores comunes: síntoma → causa raíz → solución
1) Síntoma: “Los informes son lentos” solo durante horas de negocio
Causa raíz: los informes compiten con la carga OLTP por CPU e IO; el churn del buffer pool lo amplifica.
Solución: mueve los paneles a ClickHouse; si debes quedarte en MariaDB, programa informes pesados fuera de pico y añade timeouts de consulta.
2) Síntoma: el lag de réplica de MariaDB crece cuando corre BI
Causa raíz: la réplica hace lecturas pesadas y no puede aplicar escrituras con rapidez; o el IO está saturado por escaneos.
Solución: deja de usar una réplica de replicación para analítica; delega. Si es interino, añade una “réplica analítica” dedicada con recursos aislados y acepta que sigue siendo un parche.
3) Síntoma: paneles ClickHouse a veces rápidos, a veces lentos
Causa raíz: demasiadas partes pequeñas y merges pesados; el patrón de ingestión es patológico.
Solución: agrupa inserts; reduce el número de particiones; monitoriza partes; asegura que los discos tengan margen; ajusta merges con cautela.
4) Síntoma: consulta ClickHouse lee miles de millones de filas para un filtro estrecho
Causa raíz: ORDER BY no se alinea con los predicados típicos; mal data skipping.
Solución: rediseña la clave de orden de la tabla; considera proyecciones o rollups materializados; no replique el esquema OLTP a ciegas.
5) Síntoma: los datos en ClickHouse no coinciden con MariaDB
Causa raíz: duplicados en CDC, eventos fuera de orden, borrados/actualizaciones faltantes.
Solución: usa claves idempotentes, columnas de versión y patrones de deduplicación (p. ej., ReplacingMergeTree); implementa comprobaciones de reconciliación y flujos de backfill.
6) Síntoma: ClickHouse se queda sin disco inesperadamente
Causa raíz: los merges necesitan espacio temporal; retención no aplicada; suposiciones de alta compresión fallaron por tipos de datos malos.
Solución: aplica TTL/retención; mantén disco por debajo de umbrales seguros; usa tipos correctos (LowCardinality donde corresponda) y no almacenes blobs JSON como plan principal.
7) Síntoma: consulta MariaDB “optimizó” con un índice nuevo pero la app fue más lenta
Causa raíz: índice extra incrementó amplificación de escritura; churn de cache; el optimizador eligió un plan peor para consultas OLTP.
Solución: revierte el índice; usa índices covering solo donde sean selectivos; separa la carga analítica en lugar de acumular índices.
Listas de verificación / plan paso a paso
Paso a paso: delegar analítica correctamente
-
Inventaria tu carga de reporting.
Recopila las 20 consultas principales, su frecuencia y los paneles/exportaciones que las desencadenan. -
Clasifica consultas: aptas para OLTP vs escaneos pesados analíticos.
Si una consulta examina decenas de millones de filas rutinariamente, es analítica. -
Define objetivos de frescura.
“Dentro de 5 minutos” es un requisito real; “tiempo real” es una palabra de reunión. -
Selecciona el enfoque de ingestión.
Batch ETL para reporting por hora/día; CDC para paneles casi en tiempo real. -
Diseña tablas ClickHouse desde las consultas.
Elige particionado yORDER BYsegún los patrones de filtro. -
Empieza con un dominio.
Ejemplo: analítica de events/orders, no toda la base de datos. -
Construye reconciliación.
Conteos de filas, sumas y métricas “conocidas buenas” sobre ventanas móviles. Alerta sobre deriva. -
Mueve paneles gradualmente.
Ejecuta paneles en shadow contra ClickHouse y compara resultados antes del corte final. -
Limita y protege.
Aplica timeouts y límites de concurrencia en informes de MariaDB durante la migración. -
Operationaliza ClickHouse.
Monitorización, backups, planificación de capacidad y runbooks antes de declarar victoria.
Checklist: qué configurar el primer día (mínimo)
- Slow query log de MariaDB habilitado y parseado diariamente.
- Retención del query log de ClickHouse y paneles para consultas top.
- Alertas de uso de disco con umbrales conservadores.
- Métrica de retraso de ingestión + alerta.
- Monitorización de merges/parts.
- Una política sobre quién puede ejecutar consultas ad-hoc pesadas y cómo están limitadas.
FAQ
1) ¿No puedo simplemente añadir más réplicas MariaDB y ejecutar informes allí?
Puedes, y muchos equipos lo hacen. Es un parche. Sigues usando un motor row-store OLTP para trabajo de escaneo pesado,
y aún lucharás con lag, churn de cache y hardware caro. Si el reporting es estratégico, construye un sistema analítico.
2) ¿ClickHouse puede reemplazar a MariaDB?
No para OLTP. ClickHouse puede almacenar muchos datos y responder preguntas rápido, pero no está diseñado para actualizaciones transaccionales,
restricciones estrictas y actualizaciones puntuales de alta frecuencia como MariaDB.
3) ¿Cuál es el mayor error de esquema al mover a ClickHouse?
Importar tablas OLTP normalizadas y esperar que los joins se comporten como en un data warehouse. Diseña tablas de hechos y elige un ORDER BY
alineado con tus filtros. Haz que tus consultas comunes sean baratas por construcción.
4) ¿Qué tan frescos pueden estar mis paneles con ClickHouse?
Con CDC y agrupamiento razonable, “en un minuto” es común. “En pocos segundos” es posible pero aumenta el riesgo operativo,
especialmente si conviertes la ingestión en una fábrica de partes pequeñas.
5) ¿Y los borrados y la eliminación por GDPR?
Planifícalo temprano. Para borrado verdadero, puede que necesites mutaciones dirigidas o políticas TTL según tu modelo de datos.
Si tienes requisitos legales de eliminación, no los minimices—diseña la canalización y el almacenamiento con eso en mente.
6) ¿Debemos preagregar todo con materialized views?
No. Preagrega las pocas métricas que son calientes y costosas, especialmente los tiles de panel con expectativas estrictas de latencia.
Mantén los hechos crudos para flexibilidad, pero no hagas que cada consulta de panel re-escanee datos crudos por las mismas métricas.
7) ¿Cómo evito que los analistas tumben ClickHouse con consultas malas?
Usa profiles/quotas, limita la memoria por consulta y proporciona tablas curadas para exploración común.
Además: enseña a la gente a filtrar por tiempo primero. La cultura es una característica de rendimiento.
8) ¿“Ejecutar informes en MariaDB fuera de hora” es un plan válido a largo plazo?
Solo si el negocio acepta la latencia y el crecimiento de datos se mantiene modesto. En la práctica, el reporting se expande,
“fuera de hora” desaparece a través de zonas horarias, y terminas de nuevo aquí. Delegar pronto si el reporting importa.
9) ¿Qué pasa si nuestros “informes” necesitan consistencia transaccional exacta?
Entonces define qué informes realmente la requieren. La mayoría no. Para los pocos que sí, mantenlos en MariaDB o gíralos desde
una snapshot consistente y acepta un retraso en el reporting. No obligues a todo panel a cumplir el requisito más estricto.
10) ¿Cuál es la primera victoria más simple con ClickHouse?
Delegar tablas de estilo evento (page views, auditorías, eventos de ciclo de vida de órdenes) y construir un par de tablas resumen para los paneles principales.
Verás alivio inmediato en MariaDB y rápidas mejoras en la latencia de los paneles.
Próximos pasos prácticos
Si los informes son lentos, trátalo como un problema de producción, no como una incomodidad de BI. Tu base de datos OLTP te está diciendo que está haciendo el trabajo equivocado.
Mide el dolor, identifica a los peores culpables y decide si optimizas una consulta o cambias la arquitectura.
Haz esto a continuación, en orden:
- Extrae el slow query log y ordénalo por tiempo total y filas examinadas.
- Elige un “informe asesino” representativo y confirma que es una agregación con escaneo.
- Define un objetivo de frescura y elige Batch ETL o CDC según corresponda.
- Construye una tabla de hechos en ClickHouse diseñada alrededor de los filtros y group-bys principales del panel.
- Mueve un panel, valida resultados con reconciliación y luego itera.
- Añade protecciones (quotas, límites de memoria, monitorización) antes de invitar a toda la compañía.
El objetivo no es rendir culto a ClickHouse. Es dejar de castigar a MariaDB por ser buena en transacciones.
Pon a cada sistema en su carril y obtendrás paneles más rápidos, menos incidentes y una rotación de on-call más tranquila.