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

MoteurIdéal Pour
MergeTreeAnalytique append-only, moteur de base
ReplacingMergeTreeDéduplication last-write-wins
SummingMergeTreeCompteurs et sommes pré-agrégés
AggregatingMergeTreeÉtats d’agrégation (avec vues matérialisées)
CollapsingMergeTreeLignes mutables via colonne de signe
ReplicatedMergeTreeRé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

ClickHouseTimescaleDBApache DruidDuckDBBigQueryElasticsearch
StockageColonnaireLigne + compressionSegments colonnairesColonnaireColonnaireIndex inversé
VitesseTrès rapideRapideRapideTrès rapideRapideMoyenne
IngestionTrès hauteHauteHauteFaibleHauteHaute
StreamingMoteur KafkaAggs continuesNatifNonPub/SubLogstash
Auto-hébergéOuiOuiOuiOuiNonOui
Idéal pourOLAP/logs/événementsSéries temporellesÉvénementsFichiers locauxDW serverlessRecherche 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 ... FINAL sur de grandes tables; planifiez OPTIMIZE TABLE ... FINAL en 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 BY pour les patterns de requêtes, PARTITION BY pour la rétention.
  • Les vues matérialisées + AggregatingMergeTree cré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.

Articles Connexes