Retour à l'accueil

Client ClickHouse et API HTTP : connexion et premières requêtes

Guide de toutes les façons de se connecter à ClickHouse : clickhouse-client avec des flags, fichier de configuration ~/.clickhouse-client/config.xml, API HTTP via curl. Les modes interactif et batch, les formats de réponse (JSON, CSV, Pretty, TSV) sont présentés. En utilisant une plateforme de paris comme exemple, la base de données de paris, la table bets avec les types LowCardinality et Enum8 sont créées, les données de test sont insérées via INSERT et le générateur numbers(), les SELECT avec WHERE, ORDER BY, GROUP BY sont exécutés.

Client ClickHouse : comment se connecter et travailler avec la console et HTTP
Advertisement 728x90

Client ClickHouse : Comment j'ai apprivoisé la console et l'API HTTP dans un projet de paris

Mon premier coup sur une porte fermée

Je me souviens qu'après avoir installé ClickHouse, j'ai tapé joyeusement clickhouse-client et j'ai obtenu une erreur : Code: 210. DB::NetException: Connection refused (localhost:9000). Il s'est avéré que le serveur n'écoutait que sur 127.0.0.1, et j'essayais de me connecter depuis une autre machine. Une heure de recherches Google, d'édition de config.xml, de redémarrage — et ce n'est qu'à ce moment-là que j'ai appris que le client avait les options --host et --port.

ClickHouse propose deux méthodes de communication : le client natif (pour les humains et les scripts) et l'API HTTP (pour tout le reste). J'utilise les deux quotidiennement. Voici tout ce dont vous avez vraiment besoin, ainsi que les pièges dans lesquels je suis tombé.

Méthodes de connexion : du simple au professionnel

Méthode 1. Naïve (localhost uniquement)

clickhouse-client

Cela fonctionne uniquement si vous êtes sur la même machine que le serveur et que vous n'avez pas changé le port 9000. Personne ne fait ça en production.

Google AdInline article slot

Méthode 2. Professionnelle : options pour l'accès distant

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

Ce que j'ai appris de la production : ne jamais passer un mot de passe en ligne de commande si l'historique bash est activé. Utilisez plutôt un fichier de configuration.

Méthode 3. Correcte : fichier de configuration

Créez ~/.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>

Mot de passe via variable d'environnement :

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

Pourquoi c'est plus sûr : le mot de passe n'apparaîtra pas dans ps aux ni dans l'historique. En production, nous avons eu un cas où un développeur a exécuté clickhouse-client --password secret, et en une heure, tout le monde pouvait voir le mot de passe dans les logs de l'orchestrateur.

Méthode 4. Connexion HTTP (alternative pour CI/CD)

Pour l'automatisation, j'utilise souvent HTTP :

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

Mode interactif vs batch : quand utiliser quoi

Mode interactif (pour les humains)

clickhouse-client

Avantages : autocomplétion (appuyez sur Tab), historique des commandes, requêtes multi-lignes. Inconvénients : pas pour les scripts.

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

Mon astuce : en mode interactif, des raccourcis comme \l (lister les bases de données), \d (lister les tables), \c betting (changer de base de données) fonctionnent. Tout le monde ne le sait pas, mais cela fait gagner un temps fou.

Mode batch (pour les scripts et cron)

# Commande unique
clickhouse-client --query "SELECT count() FROM betting.bets"

# Depuis un fichier
clickhouse-client --queries-file /path/to/analytics.sql

# Multi-lignes via 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

Ce qui m'a brûlé : en mode batch, terminez toujours votre requête par un point-virgule. Sans cela, la commande ne s'exécute pas, mais elle n'affiche pas non plus d'erreur — elle reste bloquée. Nous avons perdu une heure à déboguer des tâches cron.

API HTTP : curl est votre meilleur ami

L'interface HTTP écoute sur le port 8123. Elle est parfaite pour les microservices, les tableaux de bord et les scripts dans n'importe quel langage.

Requêtes GET : simples et rapides

# Requête la plus simple
curl "http://localhost:8123/?query=SELECT+version()"

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

# Avec paramètre de base de données
curl "http://localhost:8123/?database=betting&query=SELECT+count()+FROM+bets"

Requêtes POST : pour les requêtes longues et l'insertion de données

# Requête longue via POST (pas de limite de longueur d'URL)
curl -X POST "http://localhost:8123/" \
  -d "SELECT user_id, sum(amount) FROM betting.bets GROUP BY user_id"

# Insertion de données via POST
curl -X POST "http://localhost:8123/?query=INSERT+INTO+betting.bets+FORMAT+CSV" \
  --data-binary @bets_data.csv

Formats de réponse : choisissez selon votre tâche

ClickHouse peut renvoyer les données dans de nombreux formats. Je les ai tous essayés — voici ce dont vous avez vraiment besoin :

# Pretty — pour les humains (lisible mais beaucoup de caractères de formatage)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=Pretty"

# JSON — pour les API (analysé partout)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=JSON"

# JSONEachRow — pour le traitement ligne par ligne (économe en mémoire)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=JSONEachRow"

# CSV — pour l'export vers Excel/Google Sheets
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=CSV"

# TabSeparated — pour le pipe vers d'autres outils (grep, awk)
curl "http://localhost:8123/?query=SELECT+user_id,amount+FROM+bets+LIMIT+3&default_format=TSV"

Exemple concret : Nous envoyons des agrégats à un bot Telegram. Nous utilisons JSONEachRow, nous l'analysons en Python avec un simple response.json(), et nous le formatons en message.

Création d'une base de données pour une plateforme de paris

CREATE DATABASE IF NOT EXISTS betting;

Et basculez immédiatement :

clickhouse-client --database betting

Ou à l'intérieur du client :

USE betting;

Première table : schéma de pari d'un projet réel

Dans mon projet de production pour l'analyse des paris, la table ressemble à ceci :

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);

Pourquoi ainsi :

  • LowCardinality pour le sport — football, basket, tennis. Ils se répètent des milliers de fois, compressés dans un dictionnaire.
  • Enum8 pour le résultat — seulement trois valeurs, prend 1 octet au lieu d'une chaîne.
  • DateTime64(3) — les millisecondes comptent pour l'analyse des paris en direct.

Erreur courante de débutant : oublier de spécifier ENGINE = MergeTree(). Sans cela, ClickHouse crée une table avec le moteur TinyLog (test uniquement), qui ne peut pas être partitionnée et ne supporte pas la réplication. En production, insérer 10 millions de lignes dans une telle table la tuera.

Insertion de données de test

Enregistrement unique

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');

Enregistrements multiples (insertion par lots)

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');

Génération de données de test avec numbers()

Pour les tests de charge, je génère souvent un million d'enregistrements à la volée :

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);

Remarque importante : Cette insertion prendra 5 à 10 secondes sur un serveur décent. ClickHouse est optimisé pour ce genre d'opérations en masse, mais sur une VM faible, cela peut prendre une minute.

Requêtes SELECT de base dans le contexte des paris

WHERE — Filtrage

-- Paris d'un utilisateur spécifique dans la dernière heure
SELECT *
FROM betting.bets
WHERE user_id = 1001 
  AND created_at >= now() - INTERVAL 1 HOUR;

-- Paris gagnants avec une cote supérieure à 2.0
SELECT user_id, amount, odds, amount * odds AS payout
FROM betting.bets
WHERE outcome = 'win' AND odds > 2.0;

ORDER BY — Tri

-- Plus gros paris aujourd'hui
SELECT user_id, amount, created_at
FROM betting.bets
WHERE created_at >= today()
ORDER BY amount DESC
LIMIT 10;

-- 5 derniers paris d'un utilisateur
SELECT created_at, sport, amount, odds, outcome
FROM betting.bets
WHERE user_id = 1001
ORDER BY created_at DESC
LIMIT 5;

GROUP BY — Analyses

-- Gains par sport pour la semaine
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;

Combinaison de conditions

-- Utilisateurs ayant placé plus de 10 paris en un jour
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;

Cas pratique : détection de schémas suspects

Voici une requête réelle de notre système de détection 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;

Cette requête trouve les bots : ceux qui parient plus de 100 fois par heure avec des montants presque identiques. Avec PostgreSQL sur 50 millions d'enregistrements, cela ne finirait jamais. ClickHouse renvoie la réponse en 0,6 seconde.

Exportation de données : quand vous devez les donner au métier

# Export vers CSV pour le service 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

# Export vers JSON pour l'API d'un autre service
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

Erreurs courantes et leurs solutions

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

Cause : Mauvais hôte ou port, ou le serveur n'écoute pas les connexions externes.

Solution : Vérifiez netstat -tulpn | grep clickhouse. S'il n'y a pas de 0.0.0.0:9000, modifiez config.xml :

<listen_host>0.0.0.0</listen_host>

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

Cause : Vous n'avez pas créé la base de données ou n'avez pas spécifié --database.

Solution : CREATE DATABASE IF NOT EXISTS betting; ou connectez-vous avec --database betting.

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

Cause : Vous avez oublié le point-virgule en mode batch.

Solution : Mettez toujours ; à la fin de la requête lorsque vous utilisez --query.

Et ensuite ?

Maintenant vous savez comment vous connecter à ClickHouse de toutes les manières, créer des tables, insérer des données et exécuter des requêtes. Dans le prochain article, nous plongerons dans les analyses avancées : fonctions de fenêtre, tableaux, agrégations et vues matérialisées.

Tous les exemples testés sur ClickHouse 24.8. Si quelque chose ne fonctionne pas, vérifiez d'abord la version : SELECT version(); Cela m'a sauvé des centaines de fois.


Précédente:
Suivante: ClickHouse : Référence complète des types de données pour l'analyse des paris (ce qui m'a coûté cher)

— Editorial Team

Advertisement 728x90

Lire ensuite