Zurück zur Startseite

ClickHouse Materialized Views: Trigger bei INSERT

Der Artikel erklärt, dass materialisierte Ansichten in ClickHouse Trigger bei INSERT sind, kein SELECT-Cache. Er behandelt MV mit SummingMergeTree für einfache Zähler, mit AggregatingMergeTree für komplexe Aggregate, Ketten der Granularitätsreduktion (Minute→Stunde→Tag), Null+MV-Muster für Kafka, Problem historischer Daten und Gefahr von POPULATE in der Produktion. Eine vollständige Architektur für eine Glücksspielplattform mit 4 Zieltabellen und verschiedenen Engines wird bereitgestellt.

Materialized Views in ClickHouse: vollständiger Leitfaden
Advertisement 728x90

Materialisierte Sichten in ClickHouse: Die Macht der inkrementellen Verarbeitung

1. Wie MV in ClickHouse funktioniert – Es ist ein INSERT-Trigger, kein SELECT-Cache

In bekannten Datenbanken (PostgreSQL, Oracle) ist eine materialisierte Sicht (MV) das Ergebnis eines SELECT, das auf der Festplatte gespeichert und nach einem Zeitplan aktualisiert wird (REFRESH). Sie entscheiden, wann sie neu berechnet wird.

In ClickHouse ist es völlig anders. Hier ist eine MV ein INSERT-Trigger. Sie erstellen eine MV basierend auf einem SELECT, und wenn Sie Daten in die Quelltabelle einfügen, führt ClickHouse automatisch dieses SELECT über die eingefügten Zeilen aus und fügt das Ergebnis in die Zieltabelle ein.

Wesentlicher Unterschied:

Google AdInline article slot
  • In PostgreSQL: Sie aktualisieren die MV manuell oder nach einem Zeitplan und lesen die GESAMTE Quelltabelle.
  • In ClickHouse: Die MV wird bei jedem INSERT ausgelöst und verarbeitet NUR die neuen Zeilen.

Analogie aus dem echten Leben: Eine normale VIEW ist wie eine Brille mit Vergrößerungsgläsern. Sie betrachten die Quelldaten durch sie und berechnen das Ergebnis jedes Mal neu. Eine materialisierte Sicht in ClickHouse ist wie ein Sekretär, der in der Nähe sitzt und jedes Mal, wenn ein neues Dokument eintrifft, es in einen speziellen Aktenschrank im erforderlichen Format kopiert. Sie müssen nicht jedes Mal alle Dokumente durchblättern; Sie gehen einfach zum Aktenschrank.

Warum das wichtig ist: ClickHouse hat keine Trigger im klassischen Sinne. MV ist der einzige integrierte Mechanismus, um auf Dateneinfügungen zu reagieren. Damit können Sie:

  • Daten on the fly aggregieren (stündliche Summen).
  • Daten denormalisieren (Sportnamen aus einem Wörterbuch nachschlagen).
  • Dieselben Rohdaten auf mehrere Tabellen mit unterschiedlichen Strukturen verteilen (Aggregate, Deduplizierung, vollständiges Archiv).

2. Einfache MV: Wetten pro Stunde summieren

Beginnen wir mit dem häufigsten Szenario: Sie haben eine Tabelle bets mit Wetten und benötigen stündliche Statistiken nach Sportart.

Google AdInline article slot

Schritt 1: Erstellen Sie die Zieltabelle (in der die Daten aggregiert werden)

CREATE TABLE hourly_sport_stats
(
    hour        DateTime,           -- Beginn der Stunde (2025-06-01 14:00:00)
    sport_id    UInt8,              -- Sportart
    bets_count  UInt64,             -- Anzahl der Wetten pro Stunde
    total_amount Decimal(18,2)      -- Gesamtwettbetrag
)
ENGINE = SummingMergeTree()         -- Summiert nach Stunde + sport_id
ORDER BY (hour, sport_id);

Schritt 2: Erstellen Sie die MV, die diese Tabelle befüllt

CREATE MATERIALIZED VIEW mv_hourly_stats TO hourly_sport_stats AS
SELECT 
    toStartOfHour(created_at) AS hour,   -- Zeit auf den Beginn der Stunde runden
    sport_id,
    count() AS bets_count,                -- Anzahl der Wetten in der Gruppe
    sum(amount) AS total_amount           -- Summe der Wetten in der Gruppe
FROM bets
GROUP BY hour, sport_id;

Was passiert Schritt für Schritt:

Google AdInline article slot
  • TO hourly_sport_stats – wohin das Ergebnis eingefügt wird. Sie können es weglassen, wenn die MV-Struktur mit der SELECT-Struktur übereinstimmt, aber es ist besser, es explizit anzugeben.
  • AS SELECT ... – die Abfrage, die über jeden eingefügten Batch von Daten in der Tabelle bets ausgeführt wird. Nicht über die gesamte Tabelle, sondern nur über neue Zeilen.
  • GROUP BY hour, sport_id – Aggregation innerhalb des Batches. Wenn ein INSERT 1000 Zeilen für dieselbe Stunde enthält, berechnet die MV eine zusammenfassende Zeile für diese Stunde.

Wie man es verwendet:

-- 100 Wetten für 14:00 und 50 für 15:00 einfügen
INSERT INTO bets (created_at, sport_id, amount) VALUES 
    ('2025-06-01 14:15:00', 1, 100),
    ('2025-06-01 14:30:00', 1, 200),
    ('2025-06-01 15:00:00', 2, 50),
    ... (147 weitere Zeilen) ...;

-- MV fügt automatisch Zeilen in hourly_sport_stats hinzu oder aktualisiert sie
-- Für Stunde 14:00, sport_id=1: bets_count erhöht sich um die Anzahl der Wetten aus dem INSERT
-- Für Stunde 15:00, sport_id=2: ebenso

-- Jetzt Aggregate sofort lesen, ohne GROUP BY!
SELECT * FROM hourly_sport_stats 
WHERE hour = '2025-06-01 14:00:00';

Wichtige Einschränkung: MV in ClickHouse kann vorhandene Zeilen in der Zieltabelle bei wiederholten INSERTs für dieselbe Stunde nicht aktualisieren. Da wir jedoch SummingMergeTree verwenden, werden Zeilen mit demselben ORDER BY (hour, sport_id) während Hintergrund-Merges summiert. Daher kann SELECT * FROM hourly_sport_stats mehrere Zeilen pro Gruppe zurückgeben – umschließen Sie es immer mit SUM und GROUP BY, wie bei SummingMergeTree.

3. MV mit AggregatingMergeTree – Für komplexe Aggregationen

Wenn Sie nicht nur Summen, sondern auch z. B. eindeutige Benutzer (uniq), durchschnittliche Wetten (avg) benötigen, müssen Sie AggregatingMergeTree und *State / *Merge-Funktionen verwenden.

-- Zieltabelle mit AggregateFunction-Spalten
CREATE TABLE hourly_sport_advanced
(
    hour            DateTime,
    sport_id        UInt8,
    bets_count      AggregateFunction(count, UInt64),      -- Zustand für count
    total_amount    AggregateFunction(sum, Decimal(18,2)), -- Zustand für sum
    unique_users    AggregateFunction(uniq, UInt64),       -- Zustand für uniq
    avg_amount      AggregateFunction(avg, Decimal(18,2))  -- Zustand für avg
)
ENGINE = AggregatingMergeTree()
ORDER BY (hour, sport_id);

-- MV mit *State-Funktionen
CREATE MATERIALIZED VIEW mv_hourly_advanced TO hourly_sport_advanced AS
SELECT 
    toStartOfHour(created_at) AS hour,
    sport_id,
    countState(user_id) AS bets_count,        -- Nicht nur count, sondern countState
    sumState(amount) AS total_amount,
    uniqState(user_id) AS unique_users,       -- Ungefähre uniq
    avgState(amount) AS avg_amount
FROM bets
GROUP BY hour, sport_id;

Warum so: AggregatingMergeTree speichert keine endgültigen Werte, sondern Zwischenzustände. Dies ermöglicht das korrekte Zusammenführen von Aggregationen aus verschiedenen INSERTs (z. B. aus zwei Batches für dieselbe Stunde). uniqState speichert eine Hashtabelle eindeutiger Werte, nicht nur eine Zahl.

Daten lesen:

SELECT 
    hour,
    sport_id,
    countMerge(bets_count) AS bets_count,
    sumMerge(total_amount) AS total_amount,
    uniqMerge(unique_users) AS unique_users,
    avgMerge(avg_amount) AS avg_amount
FROM hourly_sport_advanced
GROUP BY hour, sport_id;

4. MV mit SummingMergeTree für einfache Zähler

Für einfache Summen und Zähler ist SummingMergeTree einfacher und schneller als AggregatingMergeTree. Das Beispiel aus Abschnitt 2 ist dafür ideal.

Ein weiteres Beispiel – Zähler nach Benutzer:

-- Zieltabelle: Wie viele Wetten jeder Benutzer pro Tag gemacht hat
CREATE TABLE user_daily_counts
(
    user_id     UInt64,
    day         Date,
    bets_count  UInt64,        -- wird summiert
    total_amount Decimal(18,2) -- wird summiert
)
ENGINE = SummingMergeTree()
ORDER BY (user_id, day);

-- MV
CREATE MATERIALIZED VIEW mv_user_daily TO user_daily_counts AS
SELECT 
    user_id,
    toDate(created_at) AS day,
    count() AS bets_count,
    sum(amount) AS total_amount
FROM bets
GROUP BY user_id, day;

Vorteil von SummingMergeTree: Einfachheit, keine Notwendigkeit für *Merge-Funktionen beim Lesen. Ein einfaches SUM mit GROUP BY reicht zur Sicherheit aus (falls Daten noch nicht gemerged wurden).

Nachteil: Nur Summen und Zähler. Keine eindeutigen Benutzer, Maximum, Durchschnitt.

5. MV-Kette: Ereignis → Minutenaggregation → Stündlich → Täglich

Dies ist ein leistungsstarkes Muster zur schrittweisen Reduzierung der Granularität. Anstatt Rohdaten direkt in tägliche Statistiken zu aggregieren (was eine Neuberechnung von Milliarden von Zeilen erfordern würde), bauen Sie eine Kette auf.

Schema:

  • Rohwetten (Tabelle raw_bets) – 7 Tage speichern.
  • Minutenaggregation (Tabelle minute_stats) – 30 Tage speichern.
  • Stündliche Aggregation (Tabelle hourly_stats) – 365 Tage speichern.
  • Tägliche Aggregation (Tabelle daily_stats) – 5 Jahre speichern.
-- Ebene 1: Minutenaggregation aus Rohdaten
CREATE TABLE minute_stats
(
    minute      DateTime,      -- auf Minute gerundet
    sport_id    UInt8,
    bets_count  UInt64,
    total_amount Decimal(18,2)
)
ENGINE = SummingMergeTree()
ORDER BY (minute, sport_id);

CREATE MATERIALIZED VIEW mv_minute_from_raw TO minute_stats AS
SELECT 
    toStartOfMinute(created_at) AS minute,
    sport_id,
    count() AS bets_count,
    sum(amount) AS total_amount
FROM raw_bets
GROUP BY minute, sport_id;

-- Ebene 2: Stündliche Aggregation aus Minuten
CREATE TABLE hourly_stats
(
    hour        DateTime,
    sport_id    UInt8,
    bets_count  UInt64,
    total_amount Decimal(18,2)
)
ENGINE = SummingMergeTree()
ORDER BY (hour, sport_id);

CREATE MATERIALIZED VIEW mv_hourly_from_minute TO hourly_stats AS
SELECT 
    toStartOfHour(minute) AS hour,   -- Minute auf Stunde runden
    sport_id,
    sum(bets_count) AS bets_count,    -- Minuten-Zähler summieren
    sum(total_amount) AS total_amount
FROM minute_stats
GROUP BY hour, sport_id;

-- Ebene 3: Tägliche Aggregation aus stündlichen
CREATE TABLE daily_stats
(
    day         Date,
    sport_id    UInt8,
    bets_count  UInt64,
    total_amount Decimal(18,2)
)
ENGINE = SummingMergeTree()
ORDER BY (day, sport_id);

CREATE MATERIALIZED VIEW mv_daily_from_hourly TO daily_stats AS
SELECT 
    toDate(hour) AS day,
    sport_id,
    sum(bets_count) AS bets_count,
    sum(total_amount) AS total_amount
FROM hourly_stats
GROUP BY day, sport_id;

Vorteile der Kette:

  • Jede Ebene arbeitet mit einem kleineren Datenvolumen.
  • Sie können alte Daten aus unteren Ebenen löschen (TTL) und Aggregate jahrelang speichern.
  • Bei der Abfrage eines Monatsberichts lesen Sie daily_stats (365 Zeilen pro Jahr), nicht raw_bets (Milliarden).

Analogie: Es ist, als würde man die Handlung eines Buches nacherzählen: Zuerst liest man das Buch (Rohdaten), dann schreibt man eine Kapitelzusammenfassung (Minute), dann eine Teilzusammenfassung (stündlich), dann eine Anmerkung (täglich). Um die Hauptidee zu erfassen, muss man nicht das gesamte Buch erneut lesen.

6. Null + MV-Muster für Kafka: Daten auf mehrere Tabellen verteilen

Dies ist ein professionelles Muster für Hochlastsysteme. Anstatt in eine Tabelle zu schreiben, erstellen Sie eine Null-Tabelle (schwarzes Loch) und mehrere MVs, die daraus lesen und in verschiedene Zieltabelle schreiben.

-- Schritt 1: Empfängertabelle (Null, speichert nichts)
CREATE TABLE raw_events_null
(
    user_id     UInt64,
    sport_id    UInt8,
    amount      Decimal(18,2),
    created_at  DateTime,
    ip          String
)
ENGINE = Null;

-- Schritt 2: Zieltabelle 1 – alle Roh-Ereignisse (für Untersuchungen)
CREATE TABLE raw_events_archive
(
    user_id     UInt64,
    sport_id    UInt8,
    amount      Decimal(18,2),
    created_at  DateTime,
    ip          String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (created_at, user_id);

-- Schritt 3: Zieltabelle 2 – deduplizierte Wetten (ohne IP für DSGVO)
CREATE TABLE bets_dedup
(
    user_id     UInt64,
    sport_id    UInt8,
    amount      Decimal(18,2),
    created_at  DateTime,
    bet_id      String
)
ENGINE = ReplacingMergeTree(created_at)
ORDER BY (user_id, bet_id);

-- Schritt 4: Zieltabelle 3 – stündliche Aggregate
CREATE TABLE hourly_agg
(
    hour        DateTime,
    sport_id    UInt8,
    total_amount AggregateFunction(sum, Decimal(18,2))
)
ENGINE = AggregatingMergeTree()
ORDER BY (hour, sport_id);

-- Schritt 5: MV – Roharchiv (alle Zeilen)
CREATE MATERIALIZED VIEW mv_archive TO raw_events_archive AS
SELECT * FROM raw_events_null;

-- Schritt 6: MV – deduplizierte Wetten (bet_id on the fly generieren)
CREATE MATERIALIZED VIEW mv_dedup TO bets_dedup AS
SELECT 
    user_id,
    sport_id,
    amount,
    created_at,
    concat(toString(user_id), '_', toString(sipHash64(created_at))) AS bet_id
FROM raw_events_null
WHERE amount != 0;

-- Schritt 7: MV – stündliche Aggregate
CREATE MATERIALIZED VIEW mv_hourly_agg TO hourly_agg AS
SELECT 
    toStartOfHour(created_at) AS hour,
    sport_id,
    sumState(amount) AS total_amount
FROM raw_events_null
GROUP BY hour, sport_id;

Wie es in der Praxis funktioniert:

-- Kafka-Consumer fügt in raw_events_null ein (schnell, keine Festplattenoperationen)
INSERT INTO raw_events_null VALUES (123, 1, 100.00, now(), '192.168.1.1');

-- Drei MVs empfangen diese Daten parallel und schreiben in ihre Tabellen:
-- 1. mv_archive → raw_events_archive (vollständige Kopie, inklusive IP)
-- 2. mv_dedup → bets_dedup (deduplizierte Wetten, ohne IP)
-- 3. mv_hourly_agg → hourly_agg (aktualisiert Aggregate)

Warum das genial ist:

  • Ein INSERT – drei verschiedene Transformationen.
  • Die Quelltabelle speichert nichts (Null), kein Schreib-Overhead.
  • Jede MV kann unabhängig aktiviert/deaktiviert werden.
  • Einfach, eine vierte MV hinzuzufügen (z. B. für den Export in ein anderes Format).

7. Das Problem: MV verarbeitet nur neue Daten nach der Erstellung

Eine kritische Einschränkung: MV sieht keine Daten, die sich zum Zeitpunkt ihrer Erstellung bereits in der Quelltabelle befanden. Die MV beginnt erst ab dem Zeitpunkt der Erstellung zu arbeiten und verarbeitet nur neue INSERTs.

-- Wir haben bereits eine Milliarde Zeilen in bets
SELECT count() FROM bets;  -- 1.000.000.000

-- MV erstellen
CREATE MATERIALIZED VIEW mv_hourly TO hourly_stats AS ...;

-- Neue Wette einfügen
INSERT INTO bets VALUES (now(), 1, 100);

-- Nur diese neue Wette erscheint in hourly_stats.
-- Die vorherige Milliarde bleibt unverarbeitet!

So lösen Sie das Problem – Nachladen historischer Daten:

-- Methode 1: Manuelles INSERT mit demselben SELECT wie in der MV
INSERT INTO hourly_stats
SELECT 
    toStartOfHour(created_at) AS hour,
    sport_id,
    count() AS bets_count,
    sum(amount) AS total_amount
FROM bets
WHERE created_at < '2025-06-01'   -- alte Daten
GROUP BY hour, sport_id;

-- Jetzt enthält hourly_stats sowohl alte als auch neue Daten

Methode 2: MV mit POPULATE neu erstellen (gefährlich!)

-- GEFÄHRLICH: Mit CREATE ... POPULATE verarbeitet die MV die GESAMTE vorhandene Tabelle
-- Während der Verarbeitung kann die Tabelle gesperrt werden, und Daten können verloren gehen
CREATE MATERIALIZED VIEW mv_hourly TO hourly_stats POPULATE AS
SELECT ... FROM bets ...;

Warum POPULATE in der Produktion gefährlich ist:

  • Während der Ausführung des SELECT kann die Quelltabelle gesperrt werden.
  • Wenn während POPULATE neue Daten in die Quelltabelle eingefügt werden, können diese verloren gehen (weder von der alten noch von der neuen MV erfasst).
  • Bei großen Tabellen kann POPULATE Stunden dauern.

Richtiger Ansatz: Erstellen Sie die MV ohne POPULATE und laden Sie historische Daten manuell über INSERT mit demselben SELECT wie in der MV nach. Dies ist sicher, vorhersagbar und blockiert keine INSERTs.

8. Überwachung und Fehlersuche bei MVs

Prüfen, ob MVs aktiv sind

-- Alle MVs und ihre Zieltabellen auflisten
SELECT 
    name,
    engine,
    total_rows,
    formatReadableSize(total_bytes) AS size
FROM system.tables
WHERE engine = 'MaterializedView'
AND database = 'default';

Prüfen, ob MV bei INSERT ausgelöst wird

-- Detailliertes Log aktivieren (nur Debug)
SET log_queries = 1;

-- Eine Zeile in die Quelltabelle einfügen
INSERT INTO bets (created_at, sport_id, amount) VALUES (now(), 1, 100);

-- Abfragen im Zusammenhang mit MV finden
SELECT 
    query,
    query_duration_ms,
    read_rows,
    written_rows
FROM system.query_log
WHERE type = 'QueryFinish'
  AND query LIKE '%MV%'
  AND event_time > now() - INTERVAL 1 MINUTE
ORDER BY event_time DESC;

Daten in der Zieltabelle prüfen

-- Anzahl der eindeutigen Gruppen in Quell- und Zieltabelle vergleichen
-- (sollte nach dem Nachladen übereinstimmen)

-- Quelle: Wie viele eindeutige (hour, sport)
SELECT count(*) FROM (
    SELECT toStartOfHour(created_at) AS hour, sport_id 
    FROM bets 
    GROUP BY hour, sport_id
) AS src;

-- Ziel:
SELECT count(*) FROM hourly_stats;

TYP-Probleme identifizieren

Wenn MV nicht ausgelöst wird, liegt das Problem oft an Typ-Inkompatibilität:

-- Fehler: Spalte `sport_id` Typ UInt8 in Quelltabelle, aber Int32 in MV-Ziel
-- Lösung: Typen im SELECT explizit casten
SELECT 
    toStartOfHour(created_at) AS hour,
    toUInt8(sport_id) AS sport_id,   -- expliziter Cast
    count() AS bets_count
FROM bets
GROUP BY hour, sport_id;

9. Echtes Schema für eine Glücksspielplattform

Lassen Sie uns eine vollständige Architektur für ein Online-Casino mit mehreren Anforderungen zusammenstellen:

  • Detaillierte Ereignisse für Untersuchungen (7 Tage speichern).
  • Deduplizierte Wetten für Analysen (90 Tage speichern).
  • Stündliche Aggregate für Dashboards (2 Jahre speichern).
  • Tägliche Aggregate für Finanzberichte (10 Jahre speichern).
-- ===== EBENE 0: Empfänger (Null) =====
CREATE TABLE raw_stream
(
    user_id     UInt64,
    sport_id    UInt8,
    bet_id      String,
    amount      Decimal(18,2),
    created_at  DateTime,
    ip          String
)
ENGINE = Null;

-- ===== ZIELTABELLEN =====
-- 1. Archiv für 7 Tage (MergeTree + TTL)
CREATE TABLE raw_archive
(
    user_id UInt64, sport_id UInt8, bet_id String,
    amount Decimal(18,2), created_at DateTime, ip String
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY created_at
TTL created_at + INTERVAL 7 DAY DELETE;

-- 2. Deduplizierte Wetten (ReplacingMergeTree)
CREATE TABLE bets_dedup
(
    user_id UInt64, sport_id UInt8, bet_id String,
    amount Decimal(18,2), created_at DateTime
)
ENGINE = ReplacingMergeTree(created_at)
ORDER BY (user_id, bet_id)
TTL created_at + INTERVAL 90 DAY DELETE;

-- 3. Stündliche Aggregate (SummingMergeTree)
CREATE TABLE hourly_agg
(
    hour DateTime, sport_id UInt8, total_amount Decimal(18,2), bet_count UInt64
)
ENGINE = SummingMergeTree()
ORDER BY (hour, sport_id)
TTL hour + INTERVAL 2 YEAR DELETE;

-- 4. Tägliche Aggregate (AggregatingMergeTree für uniq)
CREATE TABLE daily_agg
(
    day Date, sport_id UInt8, total_amount AggregateFunction(sum, Decimal(18,2)),
    unique_users AggregateFunction(uniq, UInt64)
)
ENGINE = AggregatingMergeTree()
ORDER BY (day, sport_id)
TTL day + INTERVAL 10 YEAR DELETE;

-- ===== MATERIALISIERTE SICHTEN =====
CREATE MATERIALIZED VIEW mv_raw_archive TO raw_archive AS
SELECT * FROM raw_stream;

CREATE MATERIALIZED VIEW mv_bets_dedup TO bets_dedup AS
SELECT user_id, sport_id, bet_id, amount, created_at
FROM raw_stream
WHERE bet_id != '';

CREATE MATERIALIZED VIEW mv_hourly_agg TO hourly_agg AS
SELECT 
    toStartOfHour(created_at) AS hour,
    sport_id,
    sum(amount) AS total_amount,
    count() AS bet_count
FROM raw_stream
GROUP BY hour, sport_id;

CREATE MATERIALIZED VIEW mv_daily_agg TO daily_agg AS
SELECT 
    toDate(created_at) AS day,
    sport_id,
    sumState(amount) AS total_amount,
    uniqState(user_id) AS unique_users
FROM raw_stream
GROUP BY day, sport_id;

Datenfluss:

  1. Die Anwendung schreibt in raw_stream (Null) – Millisekunden.
  2. Vier MVs verarbeiten jeden INSERT parallel.
  3. Jede Zieltabelle erhält Daten in ihrer aggregierten/transformierten Form.
  4. TTL löscht automatisch veraltete Daten auf jeder Ebene.

Vorteile gegenüber einem einzelnen MERGETREE:

  • Maximale Einfügegeschwindigkeit (Null + MV arbeiten im Speicher).
  • Unterschiedliche Speicherebenen (7 Tage, 90 Tage, 2 Jahre, 10 Jahre).
  • Unterschiedliche Engines für verschiedene Aufgaben (MergeTree, ReplacingMergeTree, SummingMergeTree, AggregatingMergeTree).
  • Kein Leistungsverlust bei Abfragen alter Aggregate.

Wie es weitergeht

Jetzt wissen Sie, wie Sie komplexe Datenverarbeitungspipelines mit MVs erstellen. Nächste Themen:

  • MV mit externen Quellaktualisierungen – wie man Wörterbücher und MVs zur Anreicherung kombiniert.
  • Fehlersuche in komplexen MV-Ketten – wie man versteht, warum Daten die Zieltabelle nicht erreicht haben.
  • MV-Replikation in einem Cluster – wie sich MVs auf verteilten Tabellen verhalten.

Zusammenfassung: Materialisierte Sichten in ClickHouse sind nicht nur ein Cache. Sie sind ein leistungsstarker Mechanismus zur inkrementellen Datenverarbeitung, der es ermöglicht, echte Event-Sourcing-Architekturen innerhalb der Datenbank zu erstellen. Die Regel ist einfach: Wenn Sie Daten bei einem INSERT aggregieren, transformieren oder duplizieren müssen, verwenden Sie MV. Und vergessen Sie nicht, historische Daten manuell nachzuladen, nicht über POPULATE in der Produktion.


Vorherige:
Nächste: Sekundäre (Skip-)Indizes in ClickHouse: Wenn der ORDER BY-Index nicht ausreicht

— Editorial Team

Advertisement 728x90

Weiterlesen