Что такое distributed tables в ClickHouse и как работает шардирование?
Distributed — движок-маршрутизатор, который при SELECT опрашивает все шарды и агрегирует результаты, а при INSERT разбивает batch по шардам согласно выражению шардирования (например, cityHash64(user_id)); физические данные хранятся в локальных MergeTree-таблицах на каждом шарде.
Distributed Tables и шардирование в ClickHouse
Distributed — это виртуальный движок таблиц в ClickHouse, который не хранит данные сам, а служит маршрутизатором запросов к локальным таблицам на других шардах кластера. Физические данные живут в таблицах с движком MergeTree (или ReplicatedMergeTree) на каждом шарде.
Архитектура
- Кластер описывается в
config.xmlили/etc/clickhouse-server/config.d/cluster.xmlсекцией<remote_servers>. - Каждый шард содержит одну или несколько реплик.
- Distributed-таблица создаётся поверх локальных таблиц и ссылается на имя кластера.
Конфигурация кластера
<remote_servers>
<my_cluster>
<shard>
<replica><host>ch-01</host><port>9000</port></replica>
<replica><host>ch-02</host><port>9000</port></replica>
</shard>
<shard>
<replica><host>ch-03</host><port>9000</port></replica>
<replica><host>ch-04</host><port>9000</port></replica>
</shard>
</my_cluster>
</remote_servers>
Создание локальной и Distributed-таблицы
-- На каждом узле: локальная таблица
CREATE TABLE events_local ON CLUSTER my_cluster
(
event_date Date,
user_id UInt64,
event_type String
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/events', '{replica}')
PARTITION BY toYYYYMM(event_date)
ORDER BY (user_id, event_date);
-- Distributed-обёртка
CREATE TABLE events ON CLUSTER my_cluster
(
event_date Date,
user_id UInt64,
event_type String
)
ENGINE = Distributed(my_cluster, default, events_local, cityHash64(user_id));
Ключ шардирования
Четвёртый аргумент Distributed() — выражение шардирования. ClickHouse берёт cityHash64(user_id) % num_shards и направляет строку на нужный шард. Если выражение опущено, используется случайное распределение (round-robin).
Как работает вставка
- При INSERT в Distributed-таблицу координирующая нода разбивает batch по шардам и асинхронно отправляет части на нужные узлы через внутренний TCP.
- Настройка
insert_distributed_sync = 1делает вставку синхронной — нода ждёт подтверждения от всех шардов. - При асинхронной вставке данные сначала буферизуются в директории
data/default/events/на инициирующей ноде.
Как работает SELECT
Координирующая нода рассылает запрос всем шардам, получает результаты и выполняет финальную агрегацию. При наличии реплик в шарде выбирается одна — по стратегии load_balancing (round_robin, nearest_hostname, random и др.).
-- Проверка распределения данных по шардам
SELECT _shard_num, count()
FROM events
GROUP BY _shard_num
ORDER BY _shard_num;
Подводные камни
- Ключ шардирования должен совпадать с частью ORDER BY — иначе агрегации по этому ключу будут требовать перегонки данных между шардами (distributed GROUP BY становится дорогим).
- Асинхронная вставка через Distributed может потерять данные при падении координирующей ноды — буфер хранится локально, а не реплицируется.
- JOIN между Distributed-таблицами выполняется через GLOBAL JOIN — без него каждый шард выполняет подзапрос независимо, что порождает N*M запросов.
- Перешардирование (изменение числа шардов) — нет встроенного решения: нужно вручную перемещать партиции командой
ALTER TABLE MOVE PARTITION. - Метаданные кластера в
config.xmlне реплицируются автоматически — конфиг нужно синхронизировать на все ноды вручную или через Ansible/Puppet. ON CLUSTERDDL выполняется асинхронно —CREATE TABLE ON CLUSTERможет завершиться на части нод, если какая-то недоступна.- При использовании
insert_distributed_sync = 0(по умолчанию) возможны дубликаты при retry вставки после таймаута. - Виртуальная колонка
_shard_numдоступна только при чтении через Distributed — нельзя использовать в локальных таблицах.
Common mistakes
- Объяснять distributed tables и sharding как OLTP-механику row-store базы вместо аналитической колоночной модели ClickHouse.
- Путать primary key ClickHouse с уникальным constraint из PostgreSQL или MySQL.
- Игнорировать parts, merges, ORDER BY, sparse index и стоимость маленьких вставок.
- Предлагать синтаксис или транзакционное поведение, которого в ClickHouse нет.
What the interviewer is testing
- Кандидат объясняет distributed tables и sharding через реальный механизм ClickHouse, а не общими словами.
- Приводит корректный SQL или диагностический запрос для этой СУБД.
- Называет ограничения, версионные отличия или эксплуатационные последствия.
- Связывает ответ с проектированием приложения, производительностью, надежностью или безопасностью.