Javier Valencia Javier Valencia
ClickHouse desde cero (I): instalación y primeros pasos

ClickHouse desde cero (I): instalación y primeros pasos

Javier Valencia · · 4 min de lectura · 6 visitas · Desarrollo
bases-de-datos tutorial clickhouse analytics clickhouse-desde-cero

Primera entrega de la serie ClickHouse desde cero a pro. Tiempo de lectura estimado: 9 minutos.

Arrancamos una serie de cinco posts sobre ClickHouse. La idea es ir de no haber tocado nunca ClickHouse a ser capaz de diseñar, optimizar y operar un cluster en producción. Nada de teoría de relleno: lo que se usa el día que tu empresa te dice "oye, tenemos 500 millones de CDRs mensuales, ¿montamos analytics?".

Si todavía tienes dudas sobre cuándo tiene sentido usar ClickHouse frente a PostgreSQL, el post ClickHouse para desarrolladores que vienen de PostgreSQL es una buena introducción previa. En esta serie doy por supuesto que ya has decidido que lo necesitas.

Qué es ClickHouse, en una frase

ClickHouse es una base de datos columnar, distribuida y orientada a analítica que habla un dialecto propio de SQL. Fue creada en Yandex para procesar los logs de Yandex.Metrica (el equivalente ruso de Google Analytics) y hoy se usa en empresas como Cloudflare, Uber, eBay o GitLab para analítica en tiempo real sobre volúmenes de cientos de miles de millones de filas.

Lo que la hace especial:

  • Compresión brutal: ratios de 10:1 o 20:1 son normales.
  • Velocidad de escaneo: cientos de millones de filas por segundo por core.
  • Escalado horizontal: sharding y replicación incorporados.
  • SQL familiar: si sabes SQL, puedes empezar el mismo día.

No está diseñada para transacciones, ni para UPDATE/DELETE frecuentes, ni para consultas por clave primaria al estilo OLTP. Eso sigue siendo trabajo de PostgreSQL o MySQL.

Opciones de instalación

Hay tres caminos razonables para empezar:

  1. Paquetes .deb / .rpm sobre un servidor Linux. La opción clásica para producción.
  2. Docker. La más rápida para probar en local.
  3. ClickHouse Cloud. El servicio gestionado oficial, con free tier.

Para esta serie vamos a usar Docker porque es reproducible y no mete ficheros en tu sistema. Todo lo que aprendas aplica igual a una instalación nativa.

Arrancar un ClickHouse local con Docker

docker run -d \
  --name clickhouse \
  -p 8123:8123 \
  -p 9000:9000 \
  -v clickhouse-data:/var/lib/clickhouse \
  --ulimit nofile=262144:262144 \
  clickhouse/clickhouse-server:latest

Algunos detalles importantes:

  • El puerto 8123 es el interfaz HTTP (útil para cURL, Grafana, drivers).
  • El puerto 9000 es el protocolo nativo (TCP binario, mucho más rápido).
  • El volumen clickhouse-data persiste los datos entre reinicios.
  • El ulimit es una recomendación oficial: ClickHouse abre muchos ficheros cuando los parts se multiplican.

Comprueba que arranca:

curl http://localhost:8123/ping
# Ok.

Y entra al cliente interactivo:

docker exec -it clickhouse clickhouse-client

Instalación nativa en Debian/Ubuntu

Para referencia, si prefieres paquetes:

sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' \
  | sudo 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" \
  | sudo tee /etc/apt/sources.list.d/clickhouse.list

sudo apt-get update
sudo apt-get install -y clickhouse-server clickhouse-client
sudo systemctl enable --now clickhouse-server

Configuración en /etc/clickhouse-server/, datos en /var/lib/clickhouse/, logs en /var/log/clickhouse-server/.

El cliente de consola

clickhouse-client es la herramienta con la que vas a pasar el 80% del tiempo mientras aprendes. Es mucho más cómodo que psql: soporta multilínea nativa, autocompletado, formatos de salida, y puede ejecutar consultas desde ficheros directamente.

Dentro del cliente, estos comandos son los que más vas a usar:

SHOW DATABASES;
SHOW TABLES FROM system;
DESCRIBE TABLE system.numbers;

La base de datos system es un pequeño tesoro: contiene metadatos sobre tablas, consultas en curso, métricas internas, merges en marcha, procesos... volveremos a ella a menudo en la entrega V.

Formatos de salida útiles al hacer consultas one-shot:

clickhouse-client --query "SELECT count() FROM system.numbers LIMIT 1000000" \
  --format PrettyCompact

clickhouse-client --query "SELECT number FROM system.numbers LIMIT 5" \
  --format JSONEachRow

PrettyCompact, TabSeparated, CSV, CSVWithNames, JSONEachRow, Parquet... ClickHouse soporta más de 70 formatos de entrada y salida. Esto es más útil de lo que parece: te permite consumir ficheros externos sin herramientas intermedias.

Tu primera tabla

Vamos con un ejemplo realista: una tabla de eventos de tráfico web.

CREATE DATABASE blog;

CREATE TABLE blog.pageviews (
  timestamp   DateTime,
  user_id     UInt64,
  path        String,
  referrer    String,
  country     LowCardinality(String),
  device      LowCardinality(String),
  duration_ms UInt32
)
ENGINE = MergeTree()
ORDER BY (timestamp, user_id);

Tres piezas importantes que vamos a diseccionar en la entrega II:

  • ENGINE = MergeTree(): el motor de tabla. MergeTree es el que vas a usar el 95% del tiempo. Define cómo se almacenan los datos en disco.
  • ORDER BY: la decisión de diseño más importante que vas a tomar. Determina el orden físico de los datos y qué consultas serán rápidas.
  • LowCardinality(String): tipo optimizado para strings con pocos valores distintos (países, dispositivos, estados). Comprime mejor y escanea más rápido.

Tu primer INSERT

ClickHouse prefiere inserts en batch grande, no fila a fila. Un antipatrón clásico es insertar una fila por petición HTTP: destroza el rendimiento y llena el disco de parts pequeñas que luego hay que mergear.

Vamos a generar datos sintéticos aprovechando system.numbers:

INSERT INTO blog.pageviews
SELECT
  now() - toIntervalSecond(number % 86400) AS timestamp,
  number % 10000 AS user_id,
  ['/', '/post/foo', '/post/bar', '/about', '/search'][number % 5 + 1] AS path,
  ['google.com', 'direct', 'twitter.com', 'hn'][number % 4 + 1] AS referrer,
  ['ES', 'FR', 'DE', 'US', 'MX'][number % 5 + 1] AS country,
  ['mobile', 'desktop', 'tablet'][number % 3 + 1] AS device,
  rand() % 30000 AS duration_ms
FROM system.numbers
LIMIT 1000000;

Un millón de filas generadas en décimas de segundo. system.numbers es una tabla virtual infinita que devuelve enteros consecutivos: una herramienta fantástica para pruebas.

Tus primeros SELECT

-- Cuántas filas tenemos
SELECT count() FROM blog.pageviews;

-- Top 5 páginas
SELECT path, count() AS views
FROM blog.pageviews
GROUP BY path
ORDER BY views DESC
LIMIT 5;

-- Tráfico por país y hora
SELECT
  country,
  toStartOfHour(timestamp) AS hour,
  count() AS views,
  avg(duration_ms) AS avg_ms
FROM blog.pageviews
GROUP BY country, hour
ORDER BY hour DESC, views DESC
LIMIT 20;

Fíjate en el tiempo de ejecución que te muestra el cliente al final de cada consulta (Elapsed: 0.024 sec.). Con un millón de filas estamos en el rango de milisegundos. Con cien millones, probablemente sigas bajo el segundo si las consultas aprovechan la clave de ordenación.

Qué ha cambiado respecto a PostgreSQL

Si vienes de PostgreSQL, en este punto notarás varias cosas raras:

  • No has creado ningún índice y las consultas vuelan.
  • No hay SERIAL, AUTO_INCREMENT ni nextval. En ClickHouse no se usan.
  • INSERT multilínea de un millón de filas tarda un suspiro y no bloquea nada.
  • No hay que hacer VACUUM ni ANALYZE.
  • Hay un tipo llamado LowCardinality que PostgreSQL no tiene.

Todo esto tiene una explicación y está conectado al diseño columnar. Lo veremos en detalle en la siguiente entrega.

Por dónde seguir

Ya tienes un ClickHouse corriendo, sabes meter datos y consultarlos. En los próximos posts de la serie:

Si estás pensando en introducir ClickHouse en un proyecto real, la comparativa con PostgreSQL te ayudará a justificar la decisión ante el equipo.