Learning Platform
Глоссарий Troubleshooting
Урок 13.07 · 30 мин
Продвинутый
max_threadsjoin_algorithmdistributed_product_modeoptimize_move_to_prewheresettingsperformance tuning

Ключевые настройки производительности

Среди сотен настроек ClickHouse есть четыре, которые напрямую определяют поведение запросов в production: max_threads, join_algorithm, distributed_product_mode, optimize_move_to_prewhere. Каждая из них влияет на разные аспекты выполнения запроса — от параллелизма чтения до правильности результатов в распределённых системах.


Обзор четырёх ключевых настроек

Ключевые настройки запросов
max_threadsmax_threads: управляет числом потоков для чтения и обработки данных. По умолчанию = числу CPU-ядер. Видно в EXPLAIN PIPELINE как '× N'. В многопользовательских системах уменьшать, чтобы не перегружать сервер concurrent запросами.
join_algorithmjoin_algorithm: выбор алгоритма JOIN. hash (по умолчанию) -- загружает правую таблицу в RAM. grace_hash -- spill на диск. full_sort_merge -- для pre-sorted. direct -- через Dictionary (до 25x быстрее). Подробно в Module 06 урок 04.
distributed_product_modedistributed_product_mode: управляет поведением подзапросов с Distributed таблицами. deny (по умолчанию) -- запрещает cross-shard subqueries. global -- отправляет подзапрос на все шарды. local -- выполняет на локальном шарде. allow -- разрешает без гарантий корректности.
optimize_move_to_prewhereoptimize_move_to_prewhere: по умолчанию 1 (включён). ClickHouse автоматически переносит подходящие условия из WHERE в PREWHERE. PREWHERE читает только столбцы фильтра для первичной отсечки, что уменьшает объём I/O. Подробно в Module 06 урок 03.

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;
WARNING

Слишком большое 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.

TIP

При установке 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'
);
WARNING

Режим 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_threadsCPU coresПараллелизм чтенияПрямо пропорционально CPU-утилизации; уменьшать при high concurrency
join_algorithmhashАлгоритм JOINКритично: hash vs grace_hash vs direct — разница до 25x
distributed_product_modedenyПодзапросы в DistributedКорректность результата; global добавляет сетевой трафик
optimize_move_to_prewhere1 (включён)Автоматический PREWHEREУменьшает I/O через предварительную фильтрацию; включён для большинства случаев

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

  1. max_threads по умолчанию равен числу CPU-ядер. В многопользовательских системах уменьшайте его до CPU_cores / max_concurrent_users, чтобы избежать CPU contention.
  2. join_algorithm = 'grace_hash' — безопасный выбор для JOIN на таблицах неизвестного размера: spill-to-disk предотвращает OOM при превышении RAM.
  3. distributed_product_mode = 'global' — корректный выбор для cross-shard подзапросов. deny (дефолт) защищает от случайных неполных результатов.
  4. optimize_move_to_prewhere = 1 (дефолт) автоматически оптимизирует WHERE через PREWHERE. Выключать только для диагностики или при редких побочных эффектах.
  5. Эти настройки применяются на уровне запроса через SET или как SETTINGS в конце SELECT — без перезапуска сервера.
Hash Join в PostgreSQL: build/probe фазы и memory overhead Spark parallelism tuning: default parallelism, shuffle partitions и стратегии

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

Результат: 0 из 0
Аналитический
Вопрос 1 из 4. ClickHouse-сервер имеет 32 CPU-ядра. На нём одновременно работают 10 аналитиков, каждый выполняет тяжёлые аналитические запросы. Какое значение max_threads разумно установить на уровне пользователя для предотвращения CPU contention?

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

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

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

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