Zurück zur Startseite

ReplacingMergeTree in ClickHouse: Vollständiger Leitfaden

Der Artikel beschreibt die ReplacingMergeTree-Engine in ClickHouse: warum sie zur Bekämpfung von Duplikaten bei unzuverlässiger Zustellung benötigt wird, wie das Merging per ORDER BY funktioniert, die Rolle der Versionierung und des FINAL-Modifikators. Typische Fallstricke, Vergleich mit CollapsingMergeTree und das Muster der materialisierten Ansicht zur Umgehung von FINAL werden diskutiert.

ReplacingMergeTree: Daten-Deduplizierung in ClickHouse
Advertisement 728x90

ReplacingMergeTree: So schlagen Sie Duplikate in ClickHouse ohne Schmerzen

1. Warum ReplacingMergeTree benötigt wird – Das reale Duplikatproblem

Stellen Sie sich vor, Sie entwickeln ein Online-Casino. Ein Spieler klickt auf den Button „Wette platzieren“ – 1000 Rubel auf Schwarz. In diesem Moment stürzt der Server, der die Anfrage verarbeitet, plötzlich ab (Überhitzung, Netzwerkfehler, wer weiß). Der Client hat keine Antwort erhalten und denkt: „Die Wette wurde nicht platziert.“ Der Spieler klickt erneut. Der Server erholt sich und akzeptiert beide Anfragen. In der Datenbank – zwei identische Wetten. Der Spieler ist wütend: 2000 Rubel wurden statt 1000 abgezogen.

Dies ist ein klassisches Idempotenz-Problem (von lateinisch idem – gleich, potens – fähig). Eine Operation ist idempotent, wenn ihre Wiederholung dasselbe Ergebnis liefert wie die einmalige Ausführung. In der Datenbankwelt benötigen wir einen Mechanismus, der erkennt: „Ich habe diese Wette bereits gesehen, ich ignoriere die zweite Version.“

In ClickHouse gibt es dafür ReplacingMergeTree. Es ist eine Tabellen-Engine, die Duplikate während des Mergens von Datenteilen automatisch entfernt. Aber ich warne Sie gleich vorweg: Es ist keine Magie – es hat Eigenheiten, die wir besprechen werden.

Google AdInline article slot

Analogie aus dem echten Leben: ReplacingMergeTree ist wie eine Sekretärin, die ein Besprechungsprotokoll führt. Leute kommen mit Anfragen zu Ihnen. Manchmal bringt derselbe Kunde zwei identische Anträge (z. B. hat er einen Zug verpasst und bittet um Rückerstattung, dann ruft er erneut mit derselben Bitte an). Die Sekretärin wirft Duplikate nicht an der Tür weg – sie legt einfach alle Papiere in einen Ordner. Einmal am Tag geht sie den Ordner durch und behält nur den neuesten Antrag von jedem Kunden. Wenn jemand vor der Sortierung fragt: „Wie viele Anträge von Ivanov?“ – sieht er zwei. Danach – einen.

2. Wie ReplacingMergeTree funktioniert – Stein für Stein

Duplikate entstehen durch unzuverlässige Zustellung

ClickHouse wurde ursprünglich für große Analysen entwickelt, bei denen ein gelegentlicher Fehler oder ein Duplikat nicht kritisch ist. Aber dann begannen die Leute, es für kritische Daten zu verwenden – und verbrannten sich. ReplacingMergeTree ist die Antwort auf diesen Schmerz.

Warum treten Duplikate überhaupt auf?

Google AdInline article slot
  • Der Client hat Daten gesendet, keine Bestätigung erhalten (Timeout) und erneut gesendet.
  • Das Warteschlangensystem (Kafka, RabbitMQ) gibt eine at-least-once-Garantie – mindestens eine Zustellung, mögliche Wiederholungen.
  • Ein Fehler im ETL-Prozess (Extract, Transform, Load) – die Pipeline wurde zweimal ausgeführt.

Mechanismus: Merge nach ORDER BY-Schlüssel

Beim Erstellen einer Tabelle mit ReplacingMergeTree müssen Sie einen Sortierschlüssel angeben – ORDER BY (spalte1, spalte2). Dies ist kein Primärschlüssel im klassischen Sinne (wie in PostgreSQL), sondern eine Möglichkeit, Daten physisch auf der Festplatte zu ordnen. ClickHouse speichert Daten in Teilen – Chunks, die nach diesem Schlüssel sortiert sind.

Wenn zwei Teile zu einem zusammengeführt werden (ein Hintergrundprozess namens Merging), scannt ReplacingMergeTree Zeilen mit demselben ORDER BY-Schlüsselwert und behält nur eine. Welche? Standardmäßig – die letzte nach Einfügezeitpunkt. Sie können aber eine numerische version-Spalte angeben, dann bleibt die Zeile mit dem maximalen Versionswert erhalten.

Git-Analogie: ReplacingMergeTree verhält sich beim Merge wie Git, wenn Sie einen Konflikt lösen: Von zwei Änderungen an derselben Datei wird die neueste behalten (wenn Sie keine explizite Strategie angeben). Nur hier ist die Datei eine Zeile in der Tabelle und der Schlüssel ist ORDER BY.

Google AdInline article slot

Versionierung: Wie ReplacingMergeTree(version) die Regeln ändert

Syntax: ReplacingMergeTree(version_column). Wenn version_column eine ganze Zahl ist (UInt* oder DateTime), bleibt die Zeile mit dem größten Wert erhalten. Dies gibt manuelle Kontrolle: Sie können explizit angeben, welche Version „gewinnt“.

Beispiel: Wir senden Wetten mit updated_at = now(). Bei einem erneuten Senden ist updated_at etwas größer. Der Merge behält die aktuellere. Wenn Sie keine version angeben, wählt ClickHouse die zuletzt eingetroffene – was möglicherweise nicht die aktuellste nach Geschäftslogik ist, sondern nur der letzte physische Einfügevorgang. Der Unterschied ist wichtig.

3. CREATE TABLE mit ReplacingMergeTree – Schritt für Schritt

-- Erstellen einer Tabelle für Wetten mit Deduplizierung
CREATE TABLE bets_dedup
(
    user_id    UInt64,           -- Spieler-ID (wem gehört die Wette)
    bet_id     String,           -- Eindeutige Wett-ID (vom Client generiert)
    amount     Decimal(10,2),    -- Betrag in Rubel
    created_at DateTime,         -- Zeitpunkt der Wetterstellung
    updated_at DateTime          -- Letzter Aktualisierungszeitpunkt (für Version)
)
ENGINE = ReplacingMergeTree(updated_at)   -- Engine mit Version nach updated_at
ORDER BY (user_id, bet_id)                -- Deduplizierungsschlüssel: (user_id, bet_id)

Was passiert Zeile für Zeile:

  • ENGINE = ReplacingMergeTree(updated_at) – gibt an, dass dies ReplacingMergeTree ist und die Spalte updated_at als Version verwendet wird. Beim Merge von zwei Zeilen mit demselben ORDER BY bleibt die mit dem größeren updated_at (neuer) erhalten. Wenn updated_at gleich ist – bleibt die zuletzt physisch eingefügte erhalten (aber es ist besser, sich nicht darauf zu verlassen).

  • ORDER BY (user_id, bet_id) – der wichtigste Parameter! Diese Menge von Spalten definiert, was als Duplikat gilt. Zwei Zeilen gelten als Duplikate, wenn sie für alle Spalten in ORDER BY dieselben Werte haben. Hier: Eine Wette von Benutzer user_id mit bet_id – ist eindeutig. Wenn zwei Zeilen mit user_id=123, bet_id='abc-456' eintreffen – werden sie zu einer zusammengeführt.

Warum ORDER BY und nicht PRIMARY KEY? In ClickHouse muss PRIMARY KEY nicht eindeutig sein. Es ist ein Hinweis für den Index, während ORDER BY die physische Reihenfolge auf der Festplatte ist. ReplacingMergeTree stützt sich auf ORDER BY, auch wenn PRIMARY KEY kürzer ist. Wenn Sie keinen PRIMARY KEY angeben, entspricht er ORDER BY.

Was ist, wenn ORDER BY zu breit ist? Zum Beispiel amount einschließen. Dann werden zwei Wetten mit unterschiedlichen Beträgen (selbst bei identischem user_id, bet_id) nicht als Duplikate betrachtet – beide bleiben erhalten. Die Deduplizierung funktioniert nicht. Fallstrick #1 (wir kommen am Ende darauf zurück).

4. Warum SELECT vor dem Merge Duplikate zurückgeben kann – und wie man damit lebt

Die Hauptnuance: ReplacingMergeTree entfernt Duplikate nur während des Merges von Datenteilen. Dies ist ein Hintergrundprozess, der nicht sofort abläuft. Zwischen dem Einfügen von Duplikaten und ihrer physischen Entfernung kann es von einigen Sekunden bis zu mehreren Stunden dauern (abhängig von Einstellungen und Last).

Was bedeutet das in der Praxis?

Fügen wir zwei Duplikate ein:

-- Erster Insert
INSERT INTO bets_dedup VALUES (123, 'bet-001', 1000, now(), now());

-- Nach 5 Sekunden – zweiter (Server hat keine Bestätigung erhalten und erneut gesendet)
INSERT INTO bets_dedup VALUES (123, 'bet-001', 1000, now(), now() + interval 5 second);

Führen Sie nun ein reguläres SELECT * FROM bets_dedup WHERE user_id = 123 aus. Was werden wir sehen? Zwei Zeilen. Weil der Merge noch nicht stattgefunden hat. Die Daten befinden sich in verschiedenen Teilen. Jeder Teil ist intern nach ORDER BY sortiert, aber Duplikate können in verschiedenen Teilen sein.

Wie erhält man garantiert eine Zeile? Verwenden Sie FINAL:

SELECT * FROM bets_dedup FINAL WHERE user_id = 123;

FINAL zwingt ClickHouse, on the fly alle Teile für diese Abfrage zu mergen und dabei die ReplacingMergeTree-Logik anzuwenden. Sie erhalten eine Zeile – mit dem maximalen updated_at (oder der letzten nach Einfügezeitpunkt, wenn keine Version angegeben ist).

Warum ist FINAL langsam? ClickHouse liest alle Teile der Tabelle, sortiert sie im Speicher nach dem ORDER BY-Schlüssel, entfernt Duplikate und gibt erst dann das Ergebnis zurück. Bei großen Tabellen (Milliarden von Zeilen) kann dies Sekunden oder Minuten dauern. Der Optimierer kann Indizes nicht effizient nutzen – er muss viele Daten scannen.

Ratschlag: Verwenden Sie FINAL nicht in Echtzeit auf großen Tabellen. Verwenden Sie es für:

  • Punktabfragen für eine einzelne user_id (der Index hilft trotzdem).
  • Hintergrundaufgaben, bei denen die Zeit nicht kritisch ist (nächtliche Berichte).
  • Kleine Tabellen (bis zu Millionen von Zeilen).

Für Produktionslast gibt es ein besseres Muster – eine materialisierte Sicht ohne FINAL.

5. Leistung von FINAL – Wann akzeptabel, wann nicht

Wann FINAL in Ordnung ist:

  • Die Tabelle ist klein (bis zu 10–20 Millionen Zeilen pro Server).
  • Sie fragen einen einzelnen Benutzer per Index ab (WHERE user_id = spezifisch).
  • Sie haben eine Hintergrundaggregation einmal pro Stunde, und 10 Sekunden Wartezeit sind in Ordnung.
  • Einmal täglicher Export für einen Bericht.

Wann FINAL ein Killer ist:

  • Tabelle >100 Millionen Zeilen.
  • Abfrage ohne Filterung (SELECT * FROM table FINAL) – ClickHouse liest alles.
  • Hochlast-OLTP-ähnliches Szenario (Dutzende Abfragen pro Sekunde mit FINAL).
  • Häufige Aktualisierungen derselben Schlüssel – viele Teile sammeln sich an, FINAL liest sie alle.

Analogie: SELECT ... FINAL ist wie das manuelle Durchsuchen aller Papiere in einem Archiv, um die neueste Version eines Dokuments zu finden, anstatt in ein spezielles „Aktuelle Versionen“-Protokoll zu schauen. Es funktioniert, aber nicht für jede Kundenanfrage.

Wie überprüft man, ob eine Abfrage FINAL verwendet?

ClickHouse hat den Befehl EXPLAIN:

EXPLAIN SELECT * FROM bets_dedup FINAL WHERE user_id = 123;

Suchen Sie nach ReadFromMergeTree mit dem final-Flag. Wenn Sie es sehen – die Abfrage durchläuft ehrlich alle Teile.

6. Muster: Hintergrundaggregation ohne FINAL mittels materialisierter Sicht

Dies ist mein Lieblingsweg, um FINAL zu umgehen. Die Idee: Lassen Sie ReplacingMergeTree sein Leben leben, Duplikate werden im Hintergrund allmählich kollabiert. Zum Lesen erstellen wir eine materialisierte Sicht, die regelmäßig neu aufgebaut wird und bereits „saubere“ Daten ohne Duplikate enthält.

Wie es aussieht:

-- 1. Basistabelle – schmutzig, mit Duplikaten
CREATE TABLE bets_raw
(
    user_id UInt64,
    bet_id String,
    amount Decimal(10,2),
    created_at DateTime,
    updated_at DateTime
)
ENGINE = ReplacingMergeTree(updated_at)
ORDER BY (user_id, bet_id);

-- 2. Zieltabelle – sauber, ohne Duplikate
CREATE TABLE bets_clean
(
    user_id UInt64,
    bet_id String,
    amount Decimal(10,2),
    created_at DateTime,
    updated_at DateTime
)
ENGINE = MergeTree()                    -- Normales MergeTree ohne Deduplizierung
ORDER BY (user_id, bet_id);

-- 3. Materialisierte Sicht – überträgt Daten beim Einfügen
CREATE MATERIALIZED VIEW bets_mv TO bets_clean AS
SELECT
    user_id,
    argMax(amount, updated_at) AS amount,      -- nimm amount aus Zeile mit max updated_at
    argMax(created_at, updated_at) AS created_at,
    max(updated_at) AS updated_at
FROM bets_raw
GROUP BY user_id, bet_id;   -- Gruppierung nach Deduplizierungsschlüssel

Wichtige Punkte erklärt:

  • argMax(amount, updated_at) – eine Aggregatfunktion, die den amount-Wert aus der Zeile mit dem größten updated_at zurückgibt. Wenn wir Duplikate mit unterschiedlichem updated_at (und unterschiedlichem amount – z. B. hat sich der Wettbetrag geändert) haben, bleibt der aktuellste Betrag erhalten. Dies ist analog zur manuellen Versionskontrolle.

  • GROUP BY user_id, bet_id – hier sagen wir explizit: „Betrachte die Kombination aus Benutzer- und Wett-ID als Duplikat.“ Jetzt muss nicht auf den Merge gewartet werden – jeder INSERT in bets_raw löst sofort (fast) eine Neuberechnung in bets_clean über bets_mv aus.

  • Wichtige Einschränkung: Materialisierte Sichten in ClickHouse verarbeiten Daten in Batches – jeden Insert separat. Wenn ein einzelner Insert zwei Duplikate (user_id, bet_id) enthält – kollabieren sie innerhalb des Batches. Wenn Duplikate in verschiedenen Inserts kommen – kann bets_clean temporäre Duplikate enthalten, bis bets_raw merged. Für perfekte Sauberkeit müssen Sie entweder FINAL beim Lesen von bets_raw verwenden oder regelmäßig OPTIMIZE TABLE bets_raw (erzwungener Merge) ausführen.

Analogie: Es ist, als hätten Sie einen Entwurf (bets_raw), in den Sie alle Korrekturen eintragen, und eine Sekretärin, die alle 5 Minuten eine saubere Kopie (bets_clean) ohne Fehler neu schreibt. Leser schauen nur auf die saubere Kopie – schnell und ohne Duplikate.

7. ReplacingMergeTree(version) mit monoton steigender Version – Update-Semantik

Ein reguläres ReplacingMergeTree behält einfach die „zuletzt angekommene“ Zeile. Das ist schlecht, wenn alte Daten nach neuen Daten ankommen können (z. B. aufgrund von Netzwerkverzögerungen). Lösung: Verwenden Sie eine version-Spalte, die monoton steigt (z. B. Zeitstempel oder Sequenz-ID).

Beispiel: Spieler-Kontostand-Tabelle mit Einzahlungshistorie

CREATE TABLE player_balance
(
    user_id        UInt64,
    transaction_id String,        -- Eindeutige Transaktions-ID (UUID)
    amount         Int64,         -- Kontostandsänderung (kann negativ sein)
    balance_after  Int64,         -- Kontostand nach Transaktion
    event_time     DateTime,      -- Ereigniszeit auf dem Client
    ingestion_time DateTime       -- Einfügezeitpunkt in ClickHouse (Version)
)
ENGINE = ReplacingMergeTree(ingestion_time)   -- Version = Einfügezeitpunkt
ORDER BY (user_id, transaction_id);

Selbst wenn die Transaktion tx-001 zweimal ankommt, aber mit unterschiedlichem ingestion_time, bleibt die später eingefügte (mit größerem ingestion_time) erhalten. Dies schützt vor „späten Duplikaten“ – wenn der erste Insert um 12:00 Uhr erfolgte, der zweite um 12:05 Uhr (Wiederholung), aber aufgrund eines Netzwerkfehlers der zweite vor dem ersten am Server ankam. Ohne Version würde der frühere (nach Einfügezeitpunkt) bleiben – was der falsche sein könnte.

Was bedeutet „monoton steigend“? Bei jedem neuen Insert muss der ingestion_time-Wert größer oder gleich den vorherigen sein. Verwenden Sie now() (aktuelle Zeit auf dem ClickHouse-Server) oder einen atomaren Zähler (z. B. von ZooKeeper). Verlassen Sie sich nicht auf die Client-Zeit – Uhren können springen.

8. Vollständiges Beispiel: Deduplizierung von Kontoguthaben-Aufladungen per transaction_id

Lassen Sie uns alles zusammenführen. Wir haben einen Mikroservice, der Kontoguthaben-Aufladungen von einem Zahlungssystem akzeptiert. Das Zahlungssystem sendet Webhooks (HTTP-Aufrufe) – manchmal Duplikate.

-- Schritt 1: Tabelle für Roh-Ereignisse erstellen
CREATE TABLE balance_events
(
    user_id        UInt64,
    transaction_id String,        -- Eindeutige ID vom Zahlungssystem
    amount         Int64,         -- +1000 Rub
    event_time     DateTime,      -- Zeitpunkt der Geldabhebung vom Benutzer
    inserted_at    DateTime DEFAULT now()  -- Automatisch beim Einfügen gesetzt
)
ENGINE = ReplacingMergeTree(inserted_at)
ORDER BY (user_id, transaction_id);   -- Deduplizierung nach Paar (Benutzer, Transaktion)

-- Schritt 2: Daten einfügen (angenommen, ein Duplikat kommt an)
INSERT INTO balance_events (user_id, transaction_id, amount, event_time) 
VALUES (1, 'pay_001', 1000, '2025-06-01 10:00:00');

-- Nach einer Minute kommt ein Duplikat an (inserted_at wird automatisch als now() + 60 Sekunden gesetzt)
INSERT INTO balance_events (user_id, transaction_id, amount, event_time) 
VALUES (1, 'pay_001', 1000, '2025-06-01 10:00:00');

-- Schritt 3: Lesen ohne FINAL – wir sehen 2 Zeilen (aber nur, wenn sie noch nicht gemerged wurden)
SELECT * FROM balance_events WHERE user_id = 1;
-- Ergebnis: zwei Zeilen mit gleichem user_id, transaction_id, amount

-- Schritt 4: Lesen mit FINAL – wir sehen eine Zeile (mit max inserted_at)
SELECT * FROM balance_events FINAL WHERE user_id = 1;
-- Ergebnis: eine Zeile

Warum reicht transaction_id in ORDER BY nicht? Weil zwei verschiedene Benutzer dieselbe transaction_id haben könnten (z. B. hat jedes Zahlungssystem seinen eigenen Zähler). Das Hinzufügen von user_id garantiert Eindeutigkeit innerhalb eines Benutzers. Wenn das System globale UUIDs generiert (550e8400-e29b-41d4-a716-446655440000) – können Sie ORDER BY transaction_id allein verwenden, eine UUID reicht aus.

9. Vergleich mit CollapsingMergeTree

CollapsingMergeTree ist eine weitere Engine für die Behandlung von Änderungen. Sie speichert „Plus“- und „Minus“-Paare und kollabiert sie während des Merges.

Wesentliche Unterschiede:

Merkmal ReplacingMergeTree CollapsingMergeTree
Mechanismus Behält eine Zeile aus Duplikaten Kollabiert Paare (+1 und -1)
Zweck Deduplizierung von Inserts Aktualisierung von Aggregaten (z. B. Warenkorb)
Version benötigt Optional (Version-Spalte) Zwingendes Sign-Flag (+1/-1)
Kann Historie gespeichert werden Ja, alle Versionen bis zum Merge Nein, Paare werden zerstört
FINAL zum Lesen Ja, ohne sind Duplikate sichtbar Ja, ohne sind nicht kollabierte Paare sichtbar

Wann ReplacingMergeTree wählen:

  • Sie müssen nur doppelte Zeilen entfernen.
  • Sie haben einen natürlichen Schlüssel zur Deduplizierung (Transaktions-ID).
  • Daten ändern sich selten (meistens Inserts).

Wann CollapsingMergeTree wählen:

  • Sie aktualisieren häufig eine aggregierte Metrik (z. B. „Anzahl der Artikel im Warenkorb“).
  • Sie müssen nur das Ergebnis speichern, nicht die Änderungshistorie.

Beispiel für CollapsingMergeTree:

CREATE TABLE cart_items
(
    user_id UInt64,
    product_id UInt64,
    quantity Int16,
    sign Int8  -- +1 (hinzufügen), -1 (entfernen)
) ENGINE = CollapsingMergeTree(sign)
ORDER BY (user_id, product_id);

Mit ReplacingMergeTree würden Sie die Zeile einfach mit einer neuen quantity-Version überschreiben – aber dann verlieren Sie die Änderungshistorie. CollapsingMergeTree ermöglicht die Berechnung der Gesamtsumme (SUM(quantity * sign)) sogar ohne FINAL.

10. Häufige Fallstricke – und wie man sie vermeidet

Fallstrick #1: ORDER BY enthält nicht alle eindeutigen Felder

-- SCHLECHT: nur user_id verwenden
CREATE TABLE bets_bad ENGINE = ReplacingMergeTree ORDER BY user_id;

-- Zwei Wetten für denselben Benutzer mit unterschiedlicher bet_id eingefügt
INSERT INTO bets_bad VALUES (1, 'bet_001', 100);
INSERT INTO bets_bad VALUES (1, 'bet_002', 200);

-- Beim Merge WERDEN SIE ZU EINER ZEILE ZUSAMMENGEFÜHRT – weil ORDER BY (user_id) gleich ist!
-- bet_002 geht verloren.

Richtig: Nehmen Sie in ORDER BY alle Spalten auf, die eine Zeile eindeutig machen – normalerweise eine Surrogat-ID (transaction_id) oder eine Kombination (user_id, bet_id).

Fallstrick #2: Naive Hoffnung auf sofortige Deduplizierung

Neulinge schreiben INSERT mit einem Duplikat und sofort SELECT ohne FINAL – sehen Duplikate. Sie werden von ClickHouse enttäuscht. Denken Sie daran: Deduplizierung ist asynchron. Wenn Sie sofortige Konsistenz benötigen – verwenden Sie FINAL oder das Muster mit materialisierter Sicht.

Fallstrick #3: Verwenden einer Version, die nicht monoton ist

-- SCHLECHT: Version ist Client-Zeit
CREATE TABLE events ENGINE = ReplacingMergeTree(client_time) ORDER BY (id);

-- Die Client-Uhr geht nach, sie senden eine alte Version nach einer neuen
-- Beim Merge bleibt die falsche (alte) Zeile erhalten

Lösung: Verwenden Sie now() auf der ClickHouse-Seite oder einen Hardware-Zähler.

Fallstrick #4: Optimismus bezüglich FINAL bei großen Datenmengen

Ich hatte einen Fall: Ein Entwickler aktivierte FINAL in allen Berichten auf einer Tabelle mit 2 Milliarden Zeilen. Abfragen begannen nach 300 Sekunden ein Timeout. Musste auf Aggregation mit GROUP BY und argMax umgeschrieben werden.

Goldene Regel: Wenn Sie mehr als 10 % einer Tabelle über FINAL lesen – machen Sie etwas falsch. Verwenden Sie materialisierte Sichten oder überdenken Sie die Architektur.

Fallstrick #5: ReplacingMergeTree ohne ORDER BY

ClickHouse lässt Sie keine Tabelle ohne ORDER BY erstellen. Sie können aber ORDER BY tuple() (leeres Tupel) angeben. Dann werden alle Zeilen in der Tabelle als Duplikate betrachtet – nach dem ersten Merge bleibt nur eine Zeile übrig. Fast nie benötigt.

Was als Nächstes kommt – Links zu verwandten Artikeln

Nachdem Sie sich nun mit ReplacingMergeTree vertraut gemacht haben, hier die nächsten Themen zur Erkundung:

  1. Wie man Merges optimiert – Einstellungen wie merge_with_ttl_timeout, number_of_free_entries_in_pool_to_lower_max_size_of_merge (klingt gruselig, ist aber nützlich).

  2. Deduplizierung auf INSERT-Ebene – die ReplicatedReplacingMergeTree-Engine mit ZooKeeper. Dies ist eine andere Ebene: Duplikate werden sofort beim Einfügen abgeschnitten, aber auf Kosten von Verzögerungen und Komplexität.

  3. Alternative: VersionedCollapsingMergeTree – ein Hybrid, der gleichzeitig Versionierung und Kollabieren unterstützt.

  4. Materialisierte Sichten im Detail – wie man mehrstufige Aggregationen aufbaut, um FINAL vollständig zu vermeiden.

Und schließlich: ReplacingMergeTree ist ein mächtiges Werkzeug, aber es geht nicht um „Duplikate sofort löschen“. Es geht um „Daten werden irgendwann sauber, und Sie arbeiten in der Zwischenzeit damit.“ Wenn Sie strikte Eindeutigkeit benötigen (wie PRIMARY KEY in PostgreSQL) – ist ClickHouse nicht die beste Wahl. Aber für 99 % der analytischen Aufgaben mit wiederholten Inserts – ist es ein Lebensretter.


Vorherige:
Nächste: SummingMergeTree und AggregatingMergeTree: Schmerzlose inkrementelle Aggregation

— Editorial Team

Advertisement 728x90

Weiterlesen