Стратегии INSERT
В распределённом кластере ClickHouse есть два основных пути для записи данных: INSERT через Distributed table (который маршрутизирует данные по шардам автоматически) и прямой INSERT в локальный ReplicatedMergeTree на конкретном узле. Выбор стратегии влияет на задержку, надёжность и нагрузку на сеть.
INSERT через Distributed table
Distributed table принимает INSERT и автоматически распределяет строки по шардам согласно функции шардирования:
-- Distributed table создана с xxHash64(user_id) как ключом шардирования
CREATE TABLE events_dist ON CLUSTER mycluster
ENGINE = Distributed('mycluster', 'db', 'events_local', xxHash64(user_id));
-- INSERT через Distributed: строки автоматически маршрутизируются
INSERT INTO events_dist VALUES
(1001, 'click', now()),
(1002, 'view', now()),
(1003, 'purchase', now());
При internal_replication=true (рекомендуемая настройка для ReplicatedMergeTree):
- Distributed table отправляет каждую строку ровно одной реплике выбранного шарда.
- ReplicatedMergeTree сам реплицирует часть на остальные реплики через лог в Keeper.
- Данные не дублируются на уровне сети Distributed → шарды.
<!-- remote_servers конфиг в config.xml -->
<remote_servers>
<mycluster>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>clickhouse-01</host>
<port>9000</port>
</replica>
<replica>
<host>clickhouse-02</host>
<port>9000</port>
</replica>
</shard>
</mycluster>
</remote_servers>
INSERT в локальный ReplicatedMergeTree
Прямой INSERT в ReplicatedMergeTree на конкретном узле даёт больший контроль:
-- Прямой INSERT на конкретный узел (clickhouse-01)
INSERT INTO db.events_local VALUES
(1001, 'click', now()),
(1003, 'purchase', now());
-- INSERT на другой узел (clickhouse-03, другой шард)
INSERT INTO db.events_local VALUES
(1002, 'view', now());
Этот подход требует:
- Внешнего балансировщика нагрузки (Nginx, HAProxy, ClickHouse Keeper load balancing)
- Логики на стороне клиента для маршрутизации строк по правильным шардам
- Знания ключа шардирования и количества шардов
Преимущества: меньше сетевых переходов (нет промежуточного Distributed узла), предсказуемая маршрутизация, полный контроль над топологией записи.
Недостатки: сложнее клиентский код, нет автоматической ребалансировки при добавлении шарда.
Sync vs Async: distributed_foreground_insert
Параметр distributed_foreground_insert (ранее insert_distributed_sync) управляет режимом отправки данных от Distributed узла к шардам:
-- Синхронный INSERT (ждёт подтверждения от шардов)
INSERT INTO events_dist SETTINGS distributed_foreground_insert = 1
VALUES (1001, 'click', now());
-- Асинхронный INSERT (буферизует локально, быстрее)
INSERT INTO events_dist SETTINGS distributed_foreground_insert = 0
VALUES (1001, 'click', now());
Ловушка: internal_replication и дублирование данных
Критически важно правильно понимать internal_replication:
internal_replication=true: Distributed table записывает данные одной реплике каждого шарда. ReplicatedMergeTree сам реплицирует через Keeper. Правильный вариант с ReplicatedMergeTree.internal_replication=false: Distributed table записывает данные всем репликам каждого шарда напрямую. При использовании с ReplicatedMergeTree это приводит к дублированию данных (дедупликация снимает часть дублей, но не гарантирована при сбоях).
Всегда устанавливайте internal_replication=true, если локальная таблица — ReplicatedMergeTree.
Изменение дефолтов в ClickHouse 26.2+
В версии 26.2 изменились параметры дедупликации, что влияет на поведение при миграции с 25.x:
replicated_deduplication_windowувеличен с 1000 до 10000 записей- Введён новый параметр
deduplicate_insert, который переопределяетinsert_deduplicate=0 - В сочетании с цепочкой Materialized Views это может создавать до 70x нагрузку на Keeper из-за увеличенного окна дедупликации
При миграции с 25.x на 26.x: явно проверьте настройки дедупликации и нагрузку на Keeper после обновления.
Ключевые выводы
- INSERT через Distributed удобен для клиентов: не нужно знать топологию шардов. Достаточно писать в одну точку.
- INSERT напрямую в ReplicatedMergeTree даёт больший контроль и меньше задержки, но требует клиентской логики маршрутизации.
- internal_replication=true обязателен при использовании ReplicatedMergeTree — иначе Distributed будет дублировать записи.
- distributed_foreground_insert=0 (async, default) снижает задержку записи за счёт буферизации. Для гарантированной видимости —
=1. - 26.2+: проверьте дедупликационные настройки при миграции —
replicated_deduplication_windowтеперь 10000, а не 1000.