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

EngineAm besten für
MergeTreeAppend-only-Analytik, Basis-Engine
ReplacingMergeTreeLast-write-wins-Deduplizierung
SummingMergeTreeVoraggregierte Zähler und Summen
AggregatingMergeTreeAggregationszustände (mit mat. Views)
CollapsingMergeTreeVeränderliche Zeilen via Vorzeichenspalte
ReplicatedMergeTreeHA-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

ClickHouseTimescaleDBApache DruidDuckDBBigQueryElasticsearch
SpeicherSpaltenorientiertZeile + KompressionSpalten-SegmenteSpaltenorientiertSpaltenorientiertInvertierter Index
Abfragegeschw.Sehr schnellSchnellSchnellSehr schnellSchnellMittel
IngestionSehr hochHochHochNiedrigHochHoch
StreamingKafka-EngineKont. AggregationenNativNeinPub/SubLogstash
Self-hostedJaJaJaJaNeinJa
Am besten fürOLAP/Logs/EventsZeitreihenEventsLokale DateienServerless DWVolltextsuche

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 ... FINAL auf großen Tabellen; planen Sie OPTIMIZE TABLE ... FINAL auß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 BY für Abfragemuster, PARTITION BY für Aufbewahrung.
  • Materialisierte Views + AggregatingMergeTree erstellen Aggregations-Pipelines mit null Latenz.
  • Die Kafka- und S3-Engines eliminieren externes ETL für Streaming- und Batch-Ingestion.

Verwandte Artikel