Ключевые настройки производительности
Среди сотен настроек ClickHouse есть четыре, которые напрямую определяют поведение запросов в production: max_threads, join_algorithm, distributed_product_mode, optimize_move_to_prewhere. Каждая из них влияет на разные аспекты выполнения запроса — от параллелизма чтения до правильности результатов в распределённых системах.
Обзор четырёх ключевых настроек
max_threads: параллелизм чтения
max_threads управляет числом потоков, которые ClickHouse использует для параллельного чтения данных и обработки. По умолчанию равен числу CPU-ядер на сервере.
-- Проверить текущее значение
SELECT value FROM system.settings WHERE name = 'max_threads';
-- Установить явное значение для запроса
SET max_threads = 8;
SELECT
user_id,
count() AS events
FROM large_events_table
GROUP BY user_id;
Значение max_threads отражается в EXPLAIN PIPELINE как × N:
EXPLAIN PIPELINE
SELECT count() FROM large_table;
-- Вывод: MergeTreeSelect × 8 означает, что данные читаются 8 потоками
-- (ReadFromMergeTree)
-- MergeTreeSelect(pool: ReadPoolParallelizeOutput, algorithm: Thread) × 8
Смотрите также: Module 06 урок 02 для детального разбора EXPLAIN PIPELINE и интерпретации параллелизма.
Когда уменьшать max_threads
В многопользовательских и многозадачных системах один запрос не должен монополизировать все CPU:
-- Сервер с 32 CPU-ядрами, 10 concurrent пользователей
-- Разумное значение: 32 / 10 ~ 3-4 потока на запрос
SET max_threads = 4;
Слишком большое max_threads при высоком числе concurrent запросов приводит к contention за CPU и фактически замедляет все запросы. Мониторинг: если среднее время выполнения растёт при увеличении конкурентности — уменьшайте max_threads.
join_algorithm: выбор алгоритма JOIN
join_algorithm определяет, каким способом ClickHouse выполняет JOIN-операции. Выбор алгоритма критически влияет на потребление памяти и производительность.
-- По умолчанию: hash join (правая таблица целиком в RAM)
SET join_algorithm = 'hash';
-- Для больших таблиц с spill на диск: grace_hash
SET join_algorithm = 'grace_hash';
SELECT l.user_id, r.country_name
FROM user_events l
JOIN countries r ON l.country_code = r.code;
Сравнение алгоритмов JOIN
| Алгоритм | Память | Скорость | Когда использовать |
|---|---|---|---|
hash | Высокая (правая в RAM) | Быстрая | Правая таблица помещается в RAM (дефолт) |
parallel_hash | Очень высокая | Быстрее hash | Большая правая таблица, достаточно RAM |
grace_hash | Настраиваемая (spill) | Переменная | Правая таблица превышает RAM |
full_sort_merge | Низкая (pre-sorted) | Конкурентна | Обе таблицы отсортированы по join key |
partial_merge | Минимальная | Медленная | Максимальная экономия RAM |
direct | Минимальная | Быстрейшая (до 25x) | Правая таблица = Dictionary/Join engine |
Для детального разбора каждого алгоритма, включая примеры и критерии выбора, смотрите Module 06 урок 04.
При установке join_algorithm = 'grace_hash' ClickHouse использует spill-to-disk автоматически при превышении памяти. Это предпочтительный выбор для JOIN на таблицах неизвестного размера в production, где OOM недопустим.
distributed_product_mode: подзапросы в Distributed таблицах
При работе с Distributed таблицами в кластерном ClickHouse подзапросы, ссылающиеся на Distributed таблицы, требуют явного указания поведения через distributed_product_mode.
-- Попытка подзапроса с Distributed таблицей (по умолчанию выдаст ошибку)
SELECT *
FROM distributed_events
WHERE user_id IN (
SELECT user_id FROM distributed_users WHERE country = 'RU'
);
-- Ошибка при distributed_product_mode = 'deny'
-- Решение: переключить на global (рекомендовано для большинства случаев)
SET distributed_product_mode = 'global';
Значения distributed_product_mode
| Режим | Поведение | Когда использовать |
|---|---|---|
deny | Запрещает cross-shard subqueries (по умолчанию) | Когда хотите явно избежать некорректных cross-shard запросов |
local | Выполняет подзапрос локально на каждом шарде | Когда данные шардированы одинаково и подзапрос корректен локально |
global | Отправляет подзапрос на все шарды, результат бродкастится | Наиболее корректный вариант для cross-shard подзапросов |
allow | Разрешает без гарантий корректности | Осторожно: может возвращать неполные результаты |
-- Режим global: подзапрос выполняется на каждом шарде,
-- результаты объединяются и бродкастятся для основного JOIN
SET distributed_product_mode = 'global';
SELECT e.user_id, e.event_type
FROM distributed_events e
WHERE e.user_id IN (
SELECT user_id FROM distributed_vip_users WHERE plan = 'premium'
);
Режим global выполняет подзапрос на всех шардах и передаёт результат каждому шарду. При большом результирующем наборе подзапроса это может создать значительный сетевой трафик. Оценивайте размер результата подзапроса перед использованием global.
optimize_move_to_prewhere: автоматический PREWHERE
optimize_move_to_prewhere (по умолчанию 1 — включён) автоматически переносит подходящие условия из WHERE в PREWHERE. PREWHERE — механизм ClickHouse, который читает только колонки-фильтры для первичной отсечки гранул, уменьшая объём I/O.
-- optimize_move_to_prewhere = 1 (по умолчанию)
-- ClickHouse автоматически оптимизирует этот запрос:
SELECT user_id, revenue
FROM orders
WHERE status = 'completed'
AND region = 'EU';
-- ClickHouse может перенести 'status = completed' в PREWHERE автоматически
-- Явное отключение (редко нужно):
SET optimize_move_to_prewhere = 0;
Для детального понимания механизма PREWHERE и критериев автоматического переноса условий смотрите Module 06 урок 03.
Когда выключать optimize_move_to_prewhere:
- Сложные WHERE с побочными эффектами (крайне редкий случай)
- Диагностика: когда нужно проверить поведение без автоматической оптимизации
Сводная таблица
| Настройка | По умолчанию | Назначение | Влияние на производительность |
|---|---|---|---|
max_threads | CPU cores | Параллелизм чтения | Прямо пропорционально CPU-утилизации; уменьшать при high concurrency |
join_algorithm | hash | Алгоритм JOIN | Критично: hash vs grace_hash vs direct — разница до 25x |
distributed_product_mode | deny | Подзапросы в Distributed | Корректность результата; global добавляет сетевой трафик |
optimize_move_to_prewhere | 1 (включён) | Автоматический PREWHERE | Уменьшает I/O через предварительную фильтрацию; включён для большинства случаев |
Ключевые выводы
max_threadsпо умолчанию равен числу CPU-ядер. В многопользовательских системах уменьшайте его доCPU_cores / max_concurrent_users, чтобы избежать CPU contention.join_algorithm = 'grace_hash'— безопасный выбор для JOIN на таблицах неизвестного размера: spill-to-disk предотвращает OOM при превышении RAM.distributed_product_mode = 'global'— корректный выбор для cross-shard подзапросов.deny(дефолт) защищает от случайных неполных результатов.optimize_move_to_prewhere = 1(дефолт) автоматически оптимизирует WHERE через PREWHERE. Выключать только для диагностики или при редких побочных эффектах.- Эти настройки применяются на уровне запроса через
SETили какSETTINGSв конце SELECT — без перезапуска сервера.