Volver al inicio

Cliente ClickHouse y API HTTP: conexión y primeras consultas

Guía de todas las formas de conectar a ClickHouse: clickhouse-client con flags, archivo de configuración ~/.clickhouse-client/config.xml, API HTTP mediante curl. Se muestran modos interactivo y por lotes, formatos de respuesta (JSON, CSV, Pretty, TSV). Usando una plataforma de apuestas como ejemplo, se crean la base de datos de apuestas, la tabla bets con tipos LowCardinality y Enum8, se insertan datos de prueba mediante INSERT y el generador numbers(), se ejecutan SELECT con WHERE, ORDER BY, GROUP BY.

Cliente ClickHouse: cómo conectarse y trabajar con la consola y HTTP
Advertisement 728x90

Cliente de ClickHouse: Cómo me hice amigo de la consola y la API HTTP en un proyecto de apuestas

Mi primer toque en una puerta cerrada

Recuerdo que después de instalar ClickHouse, escribí felizmente clickhouse-client y obtuve un error: Code: 210. DB::NetException: Connection refused (localhost:9000). Resulta que el servidor solo escuchaba en 127.0.0.1, y yo intentaba conectarme desde otra máquina. Una hora de Google, editando config.xml, reiniciando — y solo entonces aprendí que el cliente tiene las opciones --host y --port.

ClickHouse ofrece dos formas de comunicación: el cliente nativo (para humanos y scripts) y la API HTTP (para todo lo demás). Uso ambos a diario. A continuación, todo lo que realmente necesitas, más los errores que he cometido.

Métodos de conexión: De lo simple a lo correcto

Método 1. Ingenuo (solo localhost)

clickhouse-client

Esto solo funciona si estás en la misma máquina que el servidor y no has cambiado el puerto 9000. Nadie hace esto en producción.

Google AdInline article slot

Método 2. Profesional: Opciones para acceso remoto

clickhouse-client \
  --host analytics.prod.company.com \
  --port 9000 \
  --user analyst \
  --password 'StrongPass123' \
  --database betting

Lo que aprendí en producción: nunca pases una contraseña en la línea de comandos si el historial de bash está habilitado. En su lugar, usa un archivo de configuración.

Método 3. Correcto: Archivo de configuración

Crea ~/.clickhouse-client/config.xml:

<config>
    <host>clickhouse.prod.internal</host>
    <port>9000</port>
    <user>analyst</user>
    <password>${CLICKHOUSE_PASSWORD}</password>
    <database>betting</database>
    <history_file>/home/user/.clickhouse-client-history</history_file>
</config>

Contraseña mediante variable de entorno:

Google AdInline article slot
export CLICKHOUSE_PASSWORD="StrongPass123"
clickhouse-client

Por qué es más seguro: la contraseña no aparecerá en ps aux ni en el historial. En producción, tuvimos un caso donde un desarrollador ejecutó clickhouse-client --password secret, y en una hora todos podían ver la contraseña en los logs del orquestador.

Método 4. Conexión HTTP (Alternativa para CI/CD)

Para automatización, a menudo uso HTTP:

curl -u analyst:StrongPass123 \
  "http://clickhouse.prod.internal:8123/?query=SELECT+1"

Modo interactivo vs. por lotes: Cuándo usar cada uno

Modo interactivo (para humanos)

clickhouse-client

Pros: autocompletado (presiona Tab), historial de comandos, consultas multilínea. Contras: no para scripts.

Google AdInline article slot
:) SELECT user_id, sum(amount) FROM bets GROUP BY user_id LIMIT 5;

Mi truco de vida: en modo interactivo, funcionan atajos como \l (listar bases de datos), \d (listar tablas), \c betting (cambiar de base de datos). No todos lo saben, pero ahorra mucho tiempo.

Modo por lotes (para scripts y cron)

# Comando único
clickhouse-client --query "SELECT count() FROM betting.bets"

# Desde archivo
clickhouse-client --queries-file /path/to/analytics.sql

# Multilínea mediante heredoc
clickhouse-client <<SQL
SELECT 
    toDate(created_at) AS day,
    count() AS bets
FROM betting.bets
WHERE created_at >= today() - 7
GROUP BY day
ORDER BY day;
SQL

Lo que me quemó: en modo por lotes, siempre termina tu consulta con un punto y coma. Sin él, el comando no se ejecuta, pero tampoco muestra un error — simplemente se cuelga. Perdimos una hora depurando trabajos de cron.

API HTTP: curl es tu mejor amigo

La interfaz HTTP funciona en el puerto 8123. Es perfecta para microservicios, paneles y scripts en cualquier lenguaje.

Solicitudes GET: Simples y rápidas

# Consulta más simple
curl "http://localhost:8123/?query=SELECT+version()"

# Con autenticación
curl -u user:pass "http://localhost:8123/?query=SELECT+count()+FROM+betting.bets"

# Con parámetro de base de datos
curl "http://localhost:8123/?database=betting&query=SELECT+count()+FROM+bets"

Solicitudes POST: Para consultas grandes e inserción de datos

# Consulta larga mediante POST (sin límite de longitud de URL)
curl -X POST "http://localhost:8123/" \
  -d "SELECT user_id, sum(amount) FROM betting.bets GROUP BY user_id"

# Insertar datos mediante POST
curl -X POST "http://localhost:8123/?query=INSERT+INTO+betting.bets+FORMAT+CSV" \
  --data-binary @bets_data.csv

Formatos de respuesta: Elige según tu tarea

ClickHouse puede devolver datos en muchos formatos. Los he probado todos — aquí están los que realmente necesitas:

# Pretty — para humanos (legible pero muchos caracteres de formato)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=Pretty"

# JSON — para APIs (se parsea en todas partes)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=JSON"

# JSONEachRow — para procesamiento línea por línea (eficiente en memoria)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=JSONEachRow"

# CSV — para exportar a Excel/Google Sheets
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=CSV"

# TabSeparated — para tuberías a otras herramientas (grep, awk)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=TSV"

Ejemplo real: Enviamos agregados a un bot de Telegram. Usamos JSONEachRow, lo parseamos en Python con un solo response.json(), y lo formateamos en un mensaje.

Creando una base de datos para una plataforma de apuestas

CREATE DATABASE IF NOT EXISTS betting;

Y cambia a ella inmediatamente:

clickhouse-client --database betting

O dentro del cliente:

USE betting;

Primera tabla: Esquema de apuestas de un proyecto real

En mi proyecto de producción para análisis de apuestas, la tabla se ve así:

CREATE TABLE betting.bets
(
    user_id     UInt64,
    created_at  DateTime64(3),
    amount      Decimal(18, 2),
    odds        Float64,
    sport       LowCardinality(String),
    outcome     Enum8('win' = 1, 'loss' = 2, 'void' = 3),
    event_id    UInt64,
    bet_type    String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (created_at, user_id);

Por qué así:

  • LowCardinality para deporte — fútbol, baloncesto, tenis. Se repiten miles de veces, comprimidos en un diccionario.
  • Enum8 para resultado — solo tres valores, ocupa 1 byte en lugar de una cadena.
  • DateTime64(3) — los milisegundos importan para el análisis de apuestas en vivo.

Error común de principiantes: olvidar especificar ENGINE = MergeTree(). Sin él, ClickHouse crea una tabla con el motor TinyLog (solo pruebas), que no se puede particionar y no soporta replicación. En producción, insertar 10 millones de filas en esa tabla la matará.

Insertando datos de prueba

Registro único

INSERT INTO betting.bets (user_id, created_at, amount, odds, sport, outcome, event_id, bet_type)
VALUES (1001, now(), 50.00, 2.1, 'football', 'win', 50001, 'single');

Múltiples registros (inserción por lotes)

INSERT INTO betting.bets VALUES
(1002, now() - INTERVAL 1 HOUR, 100.00, 1.8, 'basketball', 'loss', 50002, 'single'),
(1003, now() - INTERVAL 2 HOUR, 200.00, 3.0, 'tennis', 'win', 50003, 'express'),
(1001, now() - INTERVAL 30 MINUTE, 75.00, 2.5, 'football', 'void', 50001, 'single');

Generando datos de prueba con numbers()

Para pruebas de carga, a menudo genero un millón de registros sobre la marcha:

INSERT INTO betting.bets
SELECT 
    number % 10000 AS user_id,
    now() - INTERVAL (number % 86400) SECOND,
    (number % 1000) / 10 + 10,
    1.5 + (number % 200) / 100,
    arrayElement(['football', 'basketball', 'tennis', 'hockey'], (number % 4) + 1),
    CAST((number % 3) + 1 AS Enum8('win' = 1, 'loss' = 2, 'void' = 3)),
    number,
    'single'
FROM numbers(1000000);

Nota importante: Esta inserción tomará de 5 a 10 segundos en un servidor decente. ClickHouse está optimizado para estas operaciones masivas, pero en una VM débil podría tomar un minuto.

Consultas SELECT básicas en el contexto de apuestas

WHERE — Filtrado

-- Apuestas de un usuario específico en la última hora
SELECT *
FROM betting.bets
WHERE user_id = 1001 
  AND created_at >= now() - INTERVAL 1 HOUR;

-- Apuestas ganadoras con cuotas mayores a 2.0
SELECT user_id, amount, odds, amount * odds AS payout
FROM betting.bets
WHERE outcome = 'win' AND odds > 2.0;

ORDER BY — Ordenación

-- Apuestas más grandes hoy
SELECT user_id, amount, created_at
FROM betting.bets
WHERE created_at >= today()
ORDER BY amount DESC
LIMIT 10;

-- Últimas 5 apuestas de un usuario
SELECT created_at, sport, amount, odds, outcome
FROM betting.bets
WHERE user_id = 1001
ORDER BY created_at DESC
LIMIT 5;

GROUP BY — Analítica

-- Pagos por deporte en la semana
SELECT 
    sport,
    count() AS total_bets,
    sum(amount) AS total_staked,
    sumIf(amount * odds, outcome = 'win') AS total_payout,
    round(total_payout / total_staked, 4) AS roi
FROM betting.bets
WHERE created_at >= today() - 7
GROUP BY sport
ORDER BY total_bets DESC;

Combinando condiciones

-- Usuarios que hicieron más de 10 apuestas en un día
SELECT 
    user_id,
    count() AS bets_count,
    sum(amount) AS total_amount
FROM betting.bets
WHERE created_at >= today()
GROUP BY user_id
HAVING bets_count > 10
ORDER BY total_amount DESC;

Caso práctico: Encontrando patrones sospechosos

Aquí hay una consulta real de nuestro sistema de detección de fraude:

WITH hourly_bets AS (
    SELECT 
        user_id,
        toStartOfHour(created_at) AS hour,
        count() AS bets_per_hour,
        avg(amount) AS avg_bet
    FROM betting.bets
    WHERE created_at >= now() - INTERVAL 3 HOUR
    GROUP BY user_id, hour
)
SELECT 
    user_id,
    max(bets_per_hour) AS max_rate,
    avg(avg_bet) AS typical_bet,
    stddevPop(avg_bet) AS bet_variance
FROM hourly_bets
GROUP BY user_id
HAVING max_rate > 100 AND bet_variance < 0.5;

Esta consulta encuentra bots: aquellos que apuestan más de 100 veces por hora con cantidades casi idénticas. En PostgreSQL con 50 millones de registros, nunca terminaría. ClickHouse devuelve la respuesta en 0.6 segundos.

Exportando datos: Cuando necesitas dárselos al negocio

# Exportar a CSV para el departamento de marketing
clickhouse-client --query "
    SELECT user_id, sum(amount) AS total_bet, count() AS bet_count
    FROM betting.bets
    WHERE created_at >= '2024-01-01'
    GROUP BY user_id
    ORDER BY total_bet DESC
    LIMIT 1000
" --format CSV > top_users.csv

# Exportar a JSON para la API de otro servicio
curl "http://localhost:8123/?query=SELECT+user_id,sum(amount)+FROM+betting.bets+GROUP+BY+user_id+LIMIT+10&default_format=JSON" \
  -o top_users.json

Errores comunes y sus soluciones

Error: Code: 102. DB::NetException: Connection refused

Causa: Host o puerto incorrectos, o el servidor no está escuchando conexiones externas.

Solución: Verifica netstat -tulpn | grep clickhouse. Si no hay 0.0.0.0:9000, edita config.xml:

<listen_host>0.0.0.0</listen_host>

Error: Code: 81. DB::Exception: Database betting doesn't exist

Causa: No creaste la base de datos o no especificaste --database.

Solución: CREATE DATABASE IF NOT EXISTS betting; o conéctate con --database betting.

Error: Code: 62. DB::Exception: Syntax error: failed at position 1

Causa: Olvidaste el punto y coma en modo por lotes.

Solución: Siempre pon ; al final de la consulta cuando uses --query.

¿Qué sigue?

Ahora sabes cómo conectarte a ClickHouse de cualquier forma, crear tablas, insertar datos y ejecutar consultas. En el próximo artículo, nos sumergiremos en analítica avanzada: funciones de ventana, arrays, agregaciones y vistas materializadas.

Todos los ejemplos probados en ClickHouse 24.8. Si algo no funciona, primero verifica la versión: SELECT version(); Esto me ha salvado cientos de veces.


Anterior:
Siguiente: ClickHouse: Referencia completa de tipos de datos para análisis de apuestas (lo que me costó caro)

— Editorial Team

Advertisement 728x90

Leer después