ClickHouse en producción: ingesta, merges y coste con 2,000 millones de filas
Nuestra experiencia ejecutando ClickHouse en producción: diseño del esquema, optimización de consultas y cómo logramos analítica en menos de un segundo sobre más de 100M de eventos de dispositivos.
Cuando empezamos a construir la capa de analítica de tracio.ai, necesitábamos una base de datos capaz de manejar nuestra carga de trabajo específica: ingerir 50,000 eventos de identificación de dispositivos por segundo, almacenar más de 2,000 millones de filas y responder consultas analíticas en menos de un segundo. Evaluamos PostgreSQL (demasiado lento para agregaciones a esta escala), Elasticsearch (demasiado caro para analítica de series temporales) y ClickHouse. ClickHouse ganó de forma contundente.
Por qué ClickHouse
ClickHouse es una base de datos OLAP orientada a columnas diseñada para analítica en tiempo real. Su ventaja clave para nuestra carga de trabajo es que lee solo las columnas necesarias para cada consulta. Cuando un analista de fraude pregunta «muéstrame la tasa de fraude por país de los últimos 7 días», ClickHouse lee solo las columnas country, timestamp y risk_score, ignorando las otras más de 40 columnas de la tabla de eventos. En una tabla de 2,000 millones de filas, esto reduce la E/S en un 95%.
ClickHouse también comprime los datos extraordinariamente bien. Nuestra tabla de eventos de 2,000 millones de filas ocupa 340 GB en disco, unos 170 bytes por fila comprimida frente a 1,2 KB por fila sin comprimir. La ratio de compresión de 7:1 significa que más datos caben en memoria, lo que se traduce directamente en consultas más rápidas.
Diseño del esquema
Nuestra tabla principal almacena una fila por cada evento de identificación:
La tabla usa el motor MergeTree, ordenado por (workspace_id, toDate(timestamp), visitor_hash). Este orden es crucial: significa que las consultas filtradas por workspace y rango de fechas leen la mínima cantidad de datos. La columna visitor_hash permite búsquedas rápidas por ID de visitante sin un índice secundario.
Elegimos LowCardinality(String) para country, device_type, browser_family y os_family porque estas columnas tienen menos de 10,000 valores distintos. ClickHouse almacena las columnas LowCardinality como enteros codificados por diccionario, reduciendo el almacenamiento en un 80% frente a las cadenas planas y acelerando las operaciones GROUP BY.
Estrategia de sharding
Fragmentamos la tabla de eventos entre 6 nodos usando un hash de workspace_id. Esto garantiza que todos los eventos de un cliente concreto estén en el mismo shard, lo que significa que la mayoría de las consultas (filtradas por workspace_id) alcanzan un único shard. Las consultas entre shards solo se necesitan para la analítica interna.
Cada shard tiene 2 réplicas para alta disponibilidad. La replicación usa el motor ReplicatedMergeTree integrado de ClickHouse con coordinación mediante ZooKeeper. La conmutación por error es automática: si un shard cae, las consultas se redirigen a la réplica sin cambios en el lado del cliente.
Canalización de ingesta
Los eventos fluyen desde nuestro topic de Kafka hacia ClickHouse a través de un servicio Go personalizado que agrupa las inserciones en lotes. Insertamos en lotes de 10,000 filas cada 500ms: esto equilibra la latencia de ingesta (menos de un segundo) con la eficiencia de inserción (ClickHouse rinde mejor con lotes grandes).
El servicio de ingesta gestiona la contrapresión con elegancia. Si ClickHouse tarda en aceptar las inserciones (durante los merges o bajo mucha carga de consultas), el servicio almacena en búfer hasta 1 millón de eventos en memoria y aplica contrapresión al consumidor de Kafka. En 18 meses de producción, nunca hemos perdido un evento.
Optimización de consultas
Vistas materializadas
Para las consultas de panel más habituales, usamos vistas materializadas que preagregan los datos. Nuestro panel de tasa de fraude, por ejemplo, lee de una vista materializada que agrega los recuentos de fraud_detected por workspace, país y hora. La vista reduce los datos escaneados para esta consulta de 2,000 millones de filas a 5M de filas.
Ordenación por proyecciones
Las proyecciones de ClickHouse nos permiten definir órdenes de clasificación alternativos para una tabla sin duplicar los datos. Añadimos una proyección ordenada por (workspace_id, visitor_hash, timestamp) para las consultas de línea temporal de visitantes. Sin la proyección, estas consultas escaneaban rangos de fechas completos. Con ella, leen solo los bloques que contienen al visitante objetivo.
Funciones aproximadas
Para las consultas de panel en las que los recuentos exactos no son críticos, usamos las funciones aproximadas de ClickHouse: uniqCombined para los recuentos de valores distintos (margen de error del 2%, 10 veces más rápido que uniqExact) y quantileTDigest para el cálculo de percentiles. El panel de analítica de fraude usa exclusivamente funciones aproximadas, lo que mantiene todas las consultas de panel por debajo de 200ms.
Cifras de rendimiento
Estos son benchmarks de consulta representativos en nuestro clúster de producción de 2,000 millones de filas:
Tasa de fraude por país, últimos 7 días: 120ms. Línea temporal de visitante (50 eventos): 8ms. Visitantes únicos por día, últimos 30 días: 340ms. Distribución de puntuación de riesgo, últimas 24 horas: 95ms. Los 100 principales dispositivos por recuento de eventos, últimos 30 días: 210ms.
Estas cifras incluyen la ida y vuelta de red desde nuestros servidores de aplicación hasta el clúster de ClickHouse. El tiempo de ejecución puro de la consulta suele ser entre un 30 y un 50% menor.
Lecciones operativas
Lección 1: monitoriza el retraso de los merges
El motor MergeTree de ClickHouse fusiona continuamente las partes pequeñas de datos en otras más grandes. Si los merges se retrasan (por una tasa de inserción alta o por contención de E/S de disco), el rendimiento de las consultas se degrada porque estas deben escanear más partes. Monitorizamos el recuento de partes por partición y alertamos cuando supera 300.
Lección 2: evita las operaciones ALTER TABLE grandes
Añadir una columna a una tabla de 2,000 millones de filas en ClickHouse es instantáneo (es solo metadatos). Pero cambiar el tipo de una columna requiere reescribir todas las partes de datos, un proceso que tardó 6 horas en nuestro clúster. Ahora tratamos el esquema como de solo anexado: las nuevas columnas se añaden libremente, pero los cambios de tipo pasan por una tabla de migración.
Lección 3: TTL con precaución
ClickHouse admite la expiración automática de datos mediante TTL. Fijamos un TTL de 90 días en nuestra tabla de eventos. El truco: la eliminación por TTL ocurre durante los merges, lo que significa que los datos eliminados pueden persistir durante horas o días después de que expire el TTL. Para la eliminación crítica en términos de cumplimiento, ejecutamos consultas ALTER TABLE DELETE explícitas de forma programada.
Coste
Nuestro clúster de ClickHouse de 6 nodos (cada nodo: 32 vCPU, 128 GB de RAM, 2 TB de NVMe) cuesta aproximadamente 8,400 dólares al mes en hosting bare metal. Almacena 2,000 millones de filas con una retención de 90 días y gestiona 50K inserciones/segundo más 200 consultas de panel concurrentes. El coste por evento almacenado es de 0,0000042 dólares: órdenes de magnitud más barato que la analítica comparable en bases de datos gestionadas en la nube.