TL;DR — Résumé Rapide
Guide complet ClickHouse: stockage colonnaire, moteurs MergeTree, réplication, vues matérialisées, ingestion de données et exemple de log analytics.
ClickHouse est un SGBD orienté colonnes open-source conçu pour les requêtes OLAP en temps réel à grande échelle. Développé par Yandex en 2016, il traite des milliards de lignes par seconde sur du matériel standard en combinant le stockage colonnaire, l’exécution vectorisée des requêtes et la compression agressive des données.
Prérequis
- Hôte Linux ou Docker avec au moins 4 Go de RAM (16 Go+ recommandé en production).
- Connaissances de base du SQL.
- Pour la réplication: ZooKeeper 3.6+ ou ClickHouse Keeper (intégré depuis v22.4).
Architecture de ClickHouse
Stockage Colonnaire
Les bases de données relationnelles traditionnelles stockent toutes les colonnes d’une ligne ensemble sur le disque. ClickHouse stocke chaque colonne dans un fichier séparé. Quand une requête touche seulement 3 colonnes sur 100, ClickHouse lit environ 3% des données. Cette affinité colonnaire permet également de meilleurs taux de compression car les valeurs similaires répétées se compactent efficacement.
Exécution Vectorisée des Requêtes
ClickHouse traite les données par lots de 8 192 à 65 536 lignes à la fois. Les instructions SIMD du CPU opèrent simultanément sur des vecteurs entiers de valeurs. Combiné à la compilation des requêtes via LLVM, les agrégations qui prendraient des minutes sur une base orientée lignes se terminent en secondes.
Famille de Moteurs MergeTree
| Moteur | Idéal Pour |
|---|---|
| MergeTree | Analytique append-only, moteur de base |
| ReplacingMergeTree | Déduplication last-write-wins |
| SummingMergeTree | Compteurs et sommes pré-agrégés |
| AggregatingMergeTree | États d’agrégation (avec vues matérialisées) |
| CollapsingMergeTree | Lignes mutables via colonne de signe |
| ReplicatedMergeTree | Réplication HA via Keeper/ZooKeeper |
Compression des Données
ClickHouse utilise LZ4 par défaut et supporte ZSTD. Les codecs par colonne se combinent: CODEC(Delta, LZ4) réduit les colonnes de timestamps de plus de 90%. Les taux de compression typiques sont de 5–15× par rapport aux données brutes.
Installation
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
Configuration Initiale
config.xml
<listen_host>0.0.0.0</listen_host>
<path>/var/lib/clickhouse/</path>
users.xml — Profils et Quotas
<profiles>
<default>
<max_memory_usage>10000000000</max_memory_usage>
<max_threads>8</max_threads>
</default>
</profiles>
Conception des Tables
CREATE TABLE evenements
(
date_evt Date,
heure_evt DateTime64(3),
user_id UInt64,
action LowCardinality(String),
revenu Decimal(18, 4)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(date_evt)
ORDER BY (action, user_id, heure_evt);
Utilisez LowCardinality(String) pour les colonnes avec moins de 10 000 valeurs distinctes. Évitez Nullable autant que possible — utilisez des valeurs sentinelles comme '' ou 0.
Ingestion de Données
# Client interactif
clickhouse-client --host=localhost --user=default --password=motdepasse
# Insertion par lot depuis CSV
clickhouse-client --query="INSERT INTO evenements FORMAT CSVWithNames" < evenements.csv
Moteur Kafka
CREATE TABLE evenements_kafka ( ... )
ENGINE = Kafka
SETTINGS kafka_broker_list = 'broker:9092',
kafka_topic_list = 'evenements',
kafka_group_name = 'clickhouse-consumer',
kafka_format = 'JSONEachRow';
CREATE MATERIALIZED VIEW evenements_mv TO evenements AS
SELECT * FROM evenements_kafka;
Vues Matérialisées
CREATE TABLE evenements_par_heure
(
action LowCardinality(String),
heure DateTime,
cnt AggregateFunction(count),
revenu AggregateFunction(sum, Decimal(18,4))
)
ENGINE = AggregatingMergeTree()
ORDER BY (action, heure);
CREATE MATERIALIZED VIEW evenements_par_heure_mv
TO evenements_par_heure AS
SELECT action,
toStartOfHour(heure_evt) AS heure,
countState() AS cnt,
sumState(revenu) AS revenu
FROM evenements
GROUP BY action, heure;
Réplication et Partitionnement
CREATE TABLE evenements ON CLUSTER mon_cluster
( ... )
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/evenements', '{replica}'
)
PARTITION BY toYYYYMM(date_evt)
ORDER BY (action, user_id, heure_evt);
CREATE TABLE evenements_dist ON CLUSTER mon_cluster
AS evenements
ENGINE = Distributed(mon_cluster, default, evenements, rand());
ClickHouse vs Alternatives
| ClickHouse | TimescaleDB | Apache Druid | DuckDB | BigQuery | Elasticsearch | |
|---|---|---|---|---|---|---|
| Stockage | Colonnaire | Ligne + compression | Segments colonnaires | Colonnaire | Colonnaire | Index inversé |
| Vitesse | Très rapide | Rapide | Rapide | Très rapide | Rapide | Moyenne |
| Ingestion | Très haute | Haute | Haute | Faible | Haute | Haute |
| Streaming | Moteur Kafka | Aggs continues | Natif | Non | Pub/Sub | Logstash |
| Auto-hébergé | Oui | Oui | Oui | Oui | Non | Oui |
| Idéal pour | OLAP/logs/événements | Séries temporelles | Événements | Fichiers locaux | DW serverless | Recherche texte |
Exemple Pratique: Analytique de Logs
CREATE TABLE nginx_logs
(
date_log Date,
heure_log DateTime64(3),
ip_client IPv4,
methode LowCardinality(String),
uri String,
status UInt16,
octets UInt64,
temps_req Float32
)
ENGINE = MergeTree()
PARTITION BY date_log
ORDER BY (status, heure_log)
TTL date_log + INTERVAL 90 DAY DELETE;
Pièges et Cas Particuliers
- FINAL est coûteux — Évitez
SELECT ... FINALsur de grandes tables; planifiezOPTIMIZE TABLE ... FINALen dehors des heures de pointe. - Les petits inserts détruisent les performances — Groupez toujours au moins 10 000 lignes par INSERT ou activez
async_insert = 1. - L’ordre des JOIN — Le côté droit du JOIN est chargé en mémoire. Placez toujours la plus petite table à droite.
- Partitionnement excessif — Partitionner par jour sur des tables à fort volume crée trop de petits fragments. Préférez des partitions mensuelles.
Résumé
- Le stockage colonnaire + vectorisé offre des agrégations 100-1000× plus rapides pour les charges analytiques.
- La famille MergeTree couvre la déduplication, la pré-agrégation et la mutabilité.
- La conception du schéma est le levier d’optimisation le plus impactant:
ORDER BYpour les patterns de requêtes,PARTITION BYpour la rétention. - Les vues matérialisées +
AggregatingMergeTreecréent des pipelines d’agrégation à latence nulle. - Les moteurs Kafka et S3 éliminent l’ETL externe pour l’ingestion en streaming et par lots.