Learning Platform
Глоссарий Troubleshooting
Урок 10.03 · 35 мин
Продвинутый
Distributed engineshard keyxxHash64shard weights

Стратегия шардирования и Distributed engine

Distributed — виртуальная таблица без данных. Она хранит только конфигурацию маршрутизации: имя кластера, базу данных, имя локальной таблицы и функцию шардирования. При вставке Distributed вычисляет целевой шард для каждой строки и перенаправляет её туда. При SELECT распределяет подзапросы по всем шардам и объединяет результаты.


Топология шардов и маршрутизация Distributed table
Distributed('mycluster', 'db', 'events_local', xxHash64(user_id))Distributed table — виртуальная таблица без данных. Хранит только конфиг: имя кластера, базы, локальной таблицы и функцию шардирования. CREATE TABLE events_dist ENGINE = Distributed('mycluster', 'db', 'events_local', xxHash64(user_id)).
shard_num = xxHash64(user_id) % num_shards
Shard 1Шард 1 (shard_num=0): принимает строки где xxHash64(user_id) % 2 = 0. Оба replica хранят одинаковые данные через ReplicatedMergeTree. Запись идёт на одну реплику (internal_replication=true), Keeper координирует копирование.
Replica 1Replica 1-1: clickhouse-01. Полная копия данных шарда 1. При чтении coordinator выбирает реплику по политике (round-robin, nearest_hostname, first_or_random). При сбое replica 1-1 — запросы идут к 1-2.
Replica 2Replica 1-2: clickhouse-03. Идентична Replica 1-1. ReplicatedMergeTree обеспечивает eventual consistency через лог репликации в Keeper.
Shard 2Шард 2 (shard_num=1): принимает строки где xxHash64(user_id) % 2 = 1. Независимые данные от Шарда 1 — горизонтальное масштабирование ёмкости. Добавление шарда = RAID-0 для данных, RAID-1 для надёжности.
Replica 1Replica 2-1: clickhouse-02. Полная копия данных шарда 2. В топологии 2S_2R итого 4 узла, каждый хранит 50% данных с одним дублем.
Replica 2Replica 2-2: clickhouse-04. Replica для Shard 2. В cross-DC топологии: replica-1 в DC-A, replica-2 в DC-B — обеспечивает DR при потере целого датацентра.
Keeper cluster3 узла (Raft quorum)ClickHouse Keeper (3 узла) координирует репликацию для всех шардов. Хранит: /clickhouse/tables/{shard}/{db}/{table} — лог репликации каждой таблицы. Требует большинства (2 из 3) для записи. Отдельные процессы от ClickHouse server для изоляции сбоев.

CREATE TABLE … ENGINE = Distributed

-- Синтаксис Distributed table
-- Параметры: cluster_name, database, local_table, sharding_key_expression
CREATE TABLE events_dist
(
    event_time   DateTime,
    user_id      UInt64,
    event_type   LowCardinality(String),
    properties   String
)
ENGINE = Distributed(
    'mycluster',     -- имя кластера из remote_servers конфига
    'db',            -- база данных где находится локальная таблица
    'events_local',  -- локальная таблица на каждом шарде
    xxHash64(user_id)  -- функция шардирования
);

Distributed table должна быть создана на каждом узле кластера. Схема должна совпадать с локальной таблицей. Распределение строк: shard_num = sharding_key_expression % num_shards.


Стратегии выбора shard key

Стратегии выбора shard key
rand()Стратегия: rand() -- равномерное распределение. Каждая строка случайно отправляется на любой шард. Плюс: нет горячих шардов, данные равномерны. Минус: запросы по user_id читают все шарды (нет colocation). Используется для логов без паттернов доступа.
xxHash64(user_id)Стратегия: xxHash64(user_id) -- пользовательская колокация. Все события одного пользователя попадают на один шард. Плюс: JOIN по user_id быстрый (локальный). Минус: если 10% пользователей генерируют 80% трафика -- шард с ними перегружен. Самая распространённая production-стратегия.
intHash64(toYYYYMM(date))Стратегия: intHash64(toYYYYMM(date)) -- временная колокация. Данные за один месяц попадают на один шард. Плюс: агрегации по месяцам быстрые. Минус: исторические данные (старые месяцы) не удаляются с горячих шардов равномерно. Используется когда запросы в основном по временным диапазонам.
user_id % NСтратегия: user_id % N (N = число шардов) -- детерминированное распределение. Предсказуемо: user_id 0 → шард 0, user_id 1 → шард 1, и т.д. Плюс: легко понять какой шард содержит данные. Минус: если пользователи с чётными ID активнее -- дисбаланс. В production предпочитают xxHash64 для лучшего распределения.
TIP

xxHash64(user_id) — наиболее распространённая production-стратегия для event-данных с user-контекстом. Хеш обеспечивает равномерное распределение, а колокация данных одного пользователя позволяет выполнять user-level агрегации без cross-shard запросов.


Shard weights

Shard weights позволяют распределять нагрузку неравномерно между шардами разной ёмкости. Шард с весом 2 получает вдвое больше строк, чем шард с весом 1.

<!-- remote_servers конфиг с shard weights -->
<remote_servers>
    <mycluster>
        <shard>
            <!-- Шард 1: ёмкий сервер -- получает вдвое больше данных -->
            <weight>2</weight>
            <internal_replication>true</internal_replication>
            <replica>
                <host>clickhouse-01</host>
                <port>9000</port>
            </replica>
        </shard>
        <shard>
            <!-- Шард 2: меньший сервер -- получает в два раза меньше данных -->
            <weight>1</weight>
            <internal_replication>true</internal_replication>
            <replica>
                <host>clickhouse-02</host>
                <port>9000</port>
            </replica>
        </shard>
    </mycluster>
</remote_servers>

При весах 2:1 из каждых 3 строк Distributed отправит 2 на шард 1 и 1 на шард 2. Это позволяет использовать серверы разного размера в одном кластере без равного распределения.


Ключевые выводы

  1. Distributed — виртуальная таблица без данных: хранит только маршрутизационный конфиг и вычисляет shard_num = sharding_key % num_shards для каждой вставки.
  2. xxHash64(user_id) — рекомендуемая стратегия для event-данных: обеспечивает равномерное распределение и колокацию данных одного пользователя на одном шарде.
  3. rand() даёт максимально равномерное распределение, но исключает colocation — каждый запрос по пользователю читает все шарды.
  4. Shard weights позволяют использовать серверы разной ёмкости: шард с <weight>2</weight> получает вдвое больше строк.
  5. Distributed table создаётся на каждом узле с такой же схемой, что и локальная таблица.
MPP-архитектура: модель shared-nothing Стратегии партиционирования в Spark

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Система аналитики событий хранит данные с полем user_id. Основные запросы — воронки и когорты по конкретному пользователю: SELECT ... WHERE user_id = 12345. Какая стратегия шардирования обеспечит лучшую производительность?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 11