TL;DR — Resumen Rápido
Guía completa de ClickHouse para analítica en tiempo real: almacenamiento columnar, motores MergeTree, replicación, vistas materializadas e ingesta de datos.
ClickHouse es un DBMS orientado a columnas de código abierto, construido para consultas OLAP en tiempo real a escala. Desarrollado por Yandex en 2016, procesa miles de millones de filas por segundo en hardware convencional combinando almacenamiento columnar, ejecución vectorizada de consultas y compresión agresiva de datos.
Requisitos Previos
- Host Linux o Docker con al menos 4 GB de RAM (16 GB+ recomendado para producción).
- Conocimientos básicos de SQL.
- Para replicación: ZooKeeper 3.6+ o ClickHouse Keeper (incluido desde v22.4).
Arquitectura de ClickHouse
Almacenamiento Columnar
Las bases de datos relacionales tradicionales almacenan todas las columnas de una fila juntas en disco. ClickHouse almacena cada columna en un archivo separado. Cuando una consulta toca solo 3 de 100 columnas, ClickHouse lee aproximadamente el 3% de los datos. Esta afinidad columnar también permite mejores tasas de compresión porque los valores similares repetidos se empaquetan de forma eficiente.
Ejecución Vectorizada de Consultas
ClickHouse procesa datos en lotes de 8,192–65,536 filas a la vez (el chunk o bloque). Las instrucciones SIMD de la CPU operan simultáneamente sobre vectores enteros de valores. Combinado con la compilación de consultas vía LLVM, las agregaciones que tardarían minutos en una base de datos orientada a filas se completan en segundos.
Familia de Motores MergeTree
| Motor | Mejor Para |
|---|---|
| MergeTree | Analítica de solo escritura, motor base |
| ReplacingMergeTree | Deduplicación last-write-wins |
| SummingMergeTree | Contadores y sumas preagregas |
| AggregatingMergeTree | Estados de agregación (con vistas materializadas) |
| CollapsingMergeTree | Filas mutables via columna de signo |
| ReplicatedMergeTree | Replicación HA via Keeper/ZooKeeper |
Compresión de Datos
ClickHouse usa LZ4 por defecto y soporta ZSTD. Los codecs por columna se apilan: CODEC(Delta, LZ4) reduce columnas de marcas de tiempo en más del 90%. Las tasas de compresión típicas son de 5–15× sobre los datos sin procesar.
Instalación
apt (Ubuntu/Debian)
apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' \
| gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg] \
https://packages.clickhouse.com/deb stable main" \
> /etc/apt/sources.list.d/clickhouse.list
apt-get update && apt-get install -y clickhouse-server clickhouse-client
systemctl enable --now clickhouse-server
Docker
docker run -d \
--name clickhouse \
--ulimit nofile=262144:262144 \
-p 8123:8123 -p 9000:9000 \
-v clickhouse-data:/var/lib/clickhouse \
clickhouse/clickhouse-server:latest
Configuración Inicial
config.xml
<listen_host>0.0.0.0</listen_host>
<path>/var/lib/clickhouse/</path>
users.xml — Perfiles y Cuotas
<profiles>
<default>
<max_memory_usage>10000000000</max_memory_usage>
<max_threads>8</max_threads>
</default>
</profiles>
Diseño de Tablas
CREATE TABLE eventos
(
fecha_evento Date,
hora_evento DateTime64(3),
user_id UInt64,
accion LowCardinality(String),
ingresos Decimal(18, 4)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(fecha_evento)
ORDER BY (accion, user_id, hora_evento);
Use LowCardinality(String) para columnas con menos de 10,000 valores distintos (códigos de país, métodos HTTP). Evite Nullable siempre que sea posible — use valores centinela como '' o 0.
Ingesta de Datos
# Cliente interactivo
clickhouse-client --host=localhost --user=default --password=secreto
# Inserción por lotes desde CSV
clickhouse-client --query="INSERT INTO eventos FORMAT CSVWithNames" < eventos.csv
Motor Kafka
CREATE TABLE eventos_kafka ( ... )
ENGINE = Kafka
SETTINGS kafka_broker_list = 'broker:9092',
kafka_topic_list = 'eventos',
kafka_group_name = 'clickhouse-consumer',
kafka_format = 'JSONEachRow';
CREATE MATERIALIZED VIEW eventos_mv TO eventos AS
SELECT * FROM eventos_kafka;
Vistas Materializadas
CREATE TABLE eventos_por_hora
(
accion LowCardinality(String),
hora DateTime,
cnt AggregateFunction(count),
ingresos AggregateFunction(sum, Decimal(18,4))
)
ENGINE = AggregatingMergeTree()
ORDER BY (accion, hora);
CREATE MATERIALIZED VIEW eventos_por_hora_mv
TO eventos_por_hora AS
SELECT accion,
toStartOfHour(hora_evento) AS hora,
countState() AS cnt,
sumState(ingresos) AS ingresos
FROM eventos
GROUP BY accion, hora;
Replicación y Fragmentación
CREATE TABLE eventos ON CLUSTER mi_cluster
( ... )
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/eventos', '{replica}'
)
PARTITION BY toYYYYMM(fecha_evento)
ORDER BY (accion, user_id, hora_evento);
CREATE TABLE eventos_dist ON CLUSTER mi_cluster
AS eventos
ENGINE = Distributed(mi_cluster, default, eventos, rand());
ClickHouse vs Alternativas
| ClickHouse | TimescaleDB | Apache Druid | DuckDB | BigQuery | Elasticsearch | |
|---|---|---|---|---|---|---|
| Almacenamiento | Columnar | Fila + compresión | Segmentos columnares | Columnar | Columnar | Índice invertido |
| Velocidad | Muy alta | Alta | Alta | Muy alta | Alta | Media |
| Ingesta | Muy alta | Alta | Alta | Baja | Alta | Alta |
| Streaming | Motor Kafka | Aggs continuas | Nativo | No | Pub/Sub | Logstash |
| Auto-alojado | Sí | Sí | Sí | Sí | No | Sí |
| Mejor para | OLAP/logs/eventos | Series temporales | Eventos | Archivos locales | DW serverless | Búsqueda texto |
Ejemplo Práctico: Analítica de Logs
CREATE TABLE nginx_logs
(
fecha_log Date,
hora_log DateTime64(3),
ip_cliente IPv4,
metodo LowCardinality(String),
uri String,
status UInt16,
bytes UInt64,
tiempo_req Float32
)
ENGINE = MergeTree()
PARTITION BY fecha_log
ORDER BY (status, hora_log)
TTL fecha_log + INTERVAL 90 DAY DELETE;
Errores Comunes
- FINAL es costoso — Evite
SELECT ... FINALen tablas grandes; programeOPTIMIZE TABLE ... FINALfuera de horas pico. - Inserts pequeños destruyen el rendimiento — Agrupe siempre al menos 10,000 filas por INSERT o active
async_insert = 1. - Orden de JOIN — El lado derecho del JOIN se carga en memoria. Coloque siempre la tabla más pequeña a la derecha.
- Particionado excesivo — Particionar por día en tablas de alto volumen crea demasiados fragmentos pequeños. Prefiera particiones mensuales.
Resumen
- El almacenamiento columnar + vectorizado ofrece agregaciones 100-1000× más rápidas para cargas analíticas.
- La familia MergeTree cubre deduplicación, preagregar y mutabilidad.
- El diseño de esquema es la palanca de ajuste más importante:
ORDER BYpara patrones de consulta,PARTITION BYpara retención. - Las vistas materializadas +
AggregatingMergeTreecrean pipelines de agregación con latencia cero. - Los motores Kafka y S3 eliminan el ETL externo para ingesta en streaming y por lotes.