TL;DR — Kurzzusammenfassung
ClickHouse-Leitfaden für Echtzeit-Analytik: Spaltenspeicher, MergeTree-Engines, Replikation, materialisierte Views, Dateningestion und Log-Analytics-Beispiel.
ClickHouse ist ein Open-Source-spaltenorientiertes DBMS, das für OLAP-Abfragen in Echtzeit im großen Maßstab entwickelt wurde. Von Yandex 2016 entwickelt, verarbeitet es Milliarden Zeilen pro Sekunde auf Standard-Hardware durch Kombination von Spaltenspeicher, vektorisierter Abfrageausführung und aggressiver Datenkompression.
Voraussetzungen
- Linux-Host oder Docker mit mindestens 4 GB RAM (16 GB+ für Produktion empfohlen).
- Grundkenntnisse in SQL.
- Für Replikation: ZooKeeper 3.6+ oder ClickHouse Keeper (seit v22.4 enthalten).
ClickHouse-Architektur
Spaltenorientierter Speicher
Traditionelle relationale Datenbanken speichern alle Spalten einer Zeile zusammen auf der Festplatte. ClickHouse speichert jede Spalte in einer separaten Datei. Wenn eine Abfrage nur 3 von 100 Spalten berührt, liest ClickHouse etwa 3% der Daten. Diese Spaltenaffinität ermöglicht auch bessere Kompressionsraten, da sich wiederholende ähnliche Werte effizient gepackt werden.
Vektorisierte Abfrageausführung
ClickHouse verarbeitet Daten in Batches von 8.192–65.536 Zeilen gleichzeitig. SIMD-CPU-Befehle arbeiten gleichzeitig auf ganzen Vektoren von Werten. Kombiniert mit Query-Kompilierung via LLVM werden Aggregationen, die auf einem zeilenorientierten Store Minuten dauern würden, in Sekunden abgeschlossen.
MergeTree-Engine-Familie
| Engine | Am besten für |
|---|---|
| MergeTree | Append-only-Analytik, Basis-Engine |
| ReplacingMergeTree | Last-write-wins-Deduplizierung |
| SummingMergeTree | Voraggregierte Zähler und Summen |
| AggregatingMergeTree | Aggregationszustände (mit mat. Views) |
| CollapsingMergeTree | Veränderliche Zeilen via Vorzeichenspalte |
| ReplicatedMergeTree | HA-Replikation via Keeper/ZooKeeper |
Datenkompression
ClickHouse verwendet standardmäßig LZ4 und unterstützt ZSTD. Codecs pro Spalte stapeln sich: CODEC(Delta, LZ4) reduziert Timestamp-Spalten um mehr als 90%. Typische Kompressionsraten sind 5–15× gegenüber Rohdaten.
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
Initiale Konfiguration
config.xml
<listen_host>0.0.0.0</listen_host>
<path>/var/lib/clickhouse/</path>
users.xml — Profile und Quotas
<profiles>
<default>
<max_memory_usage>10000000000</max_memory_usage>
<max_threads>8</max_threads>
</default>
</profiles>
Tabellendesign
CREATE TABLE ereignisse
(
ereignis_datum Date,
ereignis_zeit DateTime64(3),
user_id UInt64,
aktion LowCardinality(String),
umsatz Decimal(18, 4)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(ereignis_datum)
ORDER BY (aktion, user_id, ereignis_zeit);
Verwenden Sie LowCardinality(String) für Spalten mit weniger als 10.000 verschiedenen Werten. Vermeiden Sie Nullable soweit möglich — nutzen Sie Sentinel-Werte wie '' oder 0.
Dateningestion
# Interaktiver Client
clickhouse-client --host=localhost --user=default --password=passwort
# Batch-Insert aus CSV
clickhouse-client --query="INSERT INTO ereignisse FORMAT CSVWithNames" < ereignisse.csv
Kafka-Engine
CREATE TABLE ereignisse_kafka ( ... )
ENGINE = Kafka
SETTINGS kafka_broker_list = 'broker:9092',
kafka_topic_list = 'ereignisse',
kafka_group_name = 'clickhouse-consumer',
kafka_format = 'JSONEachRow';
CREATE MATERIALIZED VIEW ereignisse_mv TO ereignisse AS
SELECT * FROM ereignisse_kafka;
Materialisierte Views
CREATE TABLE ereignisse_pro_stunde
(
aktion LowCardinality(String),
stunde DateTime,
cnt AggregateFunction(count),
umsatz AggregateFunction(sum, Decimal(18,4))
)
ENGINE = AggregatingMergeTree()
ORDER BY (aktion, stunde);
CREATE MATERIALIZED VIEW ereignisse_pro_stunde_mv
TO ereignisse_pro_stunde AS
SELECT aktion,
toStartOfHour(ereignis_zeit) AS stunde,
countState() AS cnt,
sumState(umsatz) AS umsatz
FROM ereignisse
GROUP BY aktion, stunde;
Replikation und Sharding
CREATE TABLE ereignisse ON CLUSTER mein_cluster
( ... )
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/ereignisse', '{replica}'
)
PARTITION BY toYYYYMM(ereignis_datum)
ORDER BY (aktion, user_id, ereignis_zeit);
CREATE TABLE ereignisse_dist ON CLUSTER mein_cluster
AS ereignisse
ENGINE = Distributed(mein_cluster, default, ereignisse, rand());
ClickHouse vs Alternativen
| ClickHouse | TimescaleDB | Apache Druid | DuckDB | BigQuery | Elasticsearch | |
|---|---|---|---|---|---|---|
| Speicher | Spaltenorientiert | Zeile + Kompression | Spalten-Segmente | Spaltenorientiert | Spaltenorientiert | Invertierter Index |
| Abfragegeschw. | Sehr schnell | Schnell | Schnell | Sehr schnell | Schnell | Mittel |
| Ingestion | Sehr hoch | Hoch | Hoch | Niedrig | Hoch | Hoch |
| Streaming | Kafka-Engine | Kont. Aggregationen | Nativ | Nein | Pub/Sub | Logstash |
| Self-hosted | Ja | Ja | Ja | Ja | Nein | Ja |
| Am besten für | OLAP/Logs/Events | Zeitreihen | Events | Lokale Dateien | Serverless DW | Volltextsuche |
Praxisbeispiel: Log-Analytik
CREATE TABLE nginx_logs
(
log_datum Date,
log_zeit DateTime64(3),
client_ip IPv4,
methode LowCardinality(String),
uri String,
status UInt16,
bytes UInt64,
anfrage_ms Float32
)
ENGINE = MergeTree()
PARTITION BY log_datum
ORDER BY (status, log_zeit)
TTL log_datum + INTERVAL 90 DAY DELETE;
Fallstricke und Sonderfälle
- FINAL ist teuer — Vermeiden Sie
SELECT ... FINALauf großen Tabellen; planen SieOPTIMIZE TABLE ... FINALaußerhalb der Stoßzeiten. - Kleine Inserts zerstören die Performance — Bündeln Sie immer mindestens 10.000 Zeilen pro INSERT oder aktivieren Sie
async_insert = 1. - JOIN-Reihenfolge — Die rechte Seite des JOINs wird in den Speicher geladen. Platzieren Sie immer die kleinere Tabelle rechts.
- Übermäßige Partitionierung — Tagespartitionierung bei hochvolumigen Tabellen erzeugt zu viele kleine Parts. Bevorzugen Sie monatliche Partitionen.
Zusammenfassung
- Spaltenorientierter + vektorisierter Speicher liefert 100-1000× schnellere Aggregationen für analytische Workloads.
- Die MergeTree-Familie deckt Deduplizierung, Voraggregation und Veränderlichkeit ab.
- Das Schema-Design ist die wirkungsvollste Stellschraube:
ORDER BYfür Abfragemuster,PARTITION BYfür Aufbewahrung. - Materialisierte Views +
AggregatingMergeTreeerstellen Aggregations-Pipelines mit null Latenz. - Die Kafka- und S3-Engines eliminieren externes ETL für Streaming- und Batch-Ingestion.