Как ClickHouse выполняет запрос в параллельном режиме
ClickHouse разработан для скорости. Он выполняет запросы в высоко параллельном режиме, используя все доступные ядра CPU, распределяя данные по обработчикам и часто подводя оборудование к его предельным возможностям.
Этот гид объясняет, как работает параллелизм запросов в ClickHouse и как вы можете настроить или мониторить его для улучшения производительности при больших нагрузках.
Мы используем запрос агрегации к набору данных uk_price_paid_simple, чтобы проиллюстрировать ключевые концепции.
По шагам: Как ClickHouse параллелизует запрос агрегации
Когда ClickHouse ① выполняет запрос агрегации с фильтром на первичном ключе таблицы, он ② загружает первичный индекс в память, чтобы ③ определить, какие гранулы необходимо обработать, а какие можно безопасно пропустить:

Распределение работы по обработчикам
Выбранные данные затем динамически распределяются по n
параллельным обработчикам, которые обрабатывают данные блок за блоком в конечный результат:

Количество n
параллельных обработчиков контролируется настройкой max_threads, которая по умолчанию соответствует количеству доступных ядер CPU на сервере. В приведенном выше примере предполагается, что имеется 4
ядра.
На машине с 8
ядрами производительность обработки запросов примерно удвоится (но использование памяти также увеличится соответственно), так как больше обработчиков обрабатывают данные параллельно:

Эффективное распределение обработчиков является ключом к максимальному использованию CPU и снижению общего времени выполнения запроса.
Обработка запросов к шардированным таблицам
Когда данные таблицы распределены по нескольким серверам как шарды, каждый сервер обрабатывает свой шард параллельно. Внутри каждого сервера локальные данные обрабатываются с использованием параллельных обработчиков, как описано выше:

Сервер, который изначально получает запрос, собирает все субрезультаты из шардов и комбинирует их в итоговый глобальный результат.
Распределение нагрузки запросов по шартам позволяет горизонтально масштабировать параллелизм, особенно в средах с высоким трафиком.
В ClickHouse Cloud тот же самый параллелизм достигается через параллельные реплики, которые функционируют аналогично шартам в кластерах с отсутствующими общими ресурсами. Каждая реплика ClickHouse Cloud — это безд状態вый вычислительный узел — обрабатывает часть данных параллельно и вносит вклад в итоговый результат, как и независимый шард.
Мониторинг параллелизма запросов
Используйте эти инструменты, чтобы проверить, что ваш запрос полностью использует доступные ресурсы CPU и чтобы диагностировать, когда это не так.
Мы запускаем это на тестовом сервере с 59 ядрами CPU, что позволяет ClickHouse полностью продемонстрировать свой параллелизм запросов.
Чтобы наблюдать, как выполняется пример запроса, мы можем дать команду серверу ClickHouse вернуть все записи журнала уровня трассировки во время запроса агрегации. Для этой демонстрации мы убрали предикат запроса — в противном случае было бы обработано только 3 гранулы, чего недостаточно, чтобы ClickHouse мог использовать более чем несколько параллельных обработчиков:
Мы можем видеть, что
- ① ClickHouse нужно прочитать 3,609 гранул (указанных как метки в журналах трассировки) из 3 диапазонов данных.
- ② С 59 ядрами CPU он распределяет эту работу по 59 параллельным потокам обработки — по одному на каждый обработчик.
В качестве альтернативы, мы можем использовать EXPLAIN для проверки физического плана оператора — также известного как "конвейер запросов" — для запроса агрегации:
Примечание: Читайте операторный план выше снизу вверх. Каждая строка представляет собой этап в физическом плане выполнения, начиная с чтения данных из хранилища внизу и заканчивая итоговыми шагами обработки вверху. Операторы, помеченные × 59
, выполняются одновременно на не накладывающихся регионах данных через 59 параллельных обработчиков. Это отражает значение max_threads
и иллюстрирует, как каждый этап запроса параллелизуется по ядрам CPU.
Веб-интерфейс встроенного UI ClickHouse (доступный по конечной точке /play
) может отобразить физический план выше в виде графической визуализации. В этом примере мы установили max_threads
равным 4
, чтобы сохранить визуализацию компактной, показывая всего 4 параллельных обработчика:

Примечание: Читайте визуализацию слева направо. Каждая строка представляет собой параллельный обработчик, который передает данные блоками, применяя такие преобразования, как фильтрация, агрегация и завершение обработки. В этом примере вы можете увидеть четыре параллельных потока, соответствующих настройке max_threads = 4
.
Балансировка нагрузки по обработчикам
Обратите внимание, что операторы Resize
в физическом плане выше перераспределяют и перераспределяют потоки данных блоков по обработчикам, чтобы поддерживать их равномерное использование. Это перераспределение особенно важно, когда диапазоны данных различаются по количеству строк, соответствующих предикатам запроса, в противном случае некоторые обработчики могут перегружаться, в то время как другие остаются без дела. Перераспределяя работу, более быстрые обработчики эффективно помогают медленным, оптимизируя общее время выполнения запроса.
Почему max_threads не всегда соблюдается
Как упоминалось выше, количество n
параллельных обработчиков контролируется настройкой max_threads
, которая по умолчанию равняется количеству доступных ядер CPU в ClickHouse на сервере:
Однако значение max_threads
может быть проигнорировано в зависимости от объема данных, выбранного для обработки:
Как показано в извлечении из операционного плана выше, хотя max_threads
установлено на 59
, ClickHouse использует только 30 параллельных потоков для сканирования данных.
Теперь запустим запрос:
Как показано в выводе выше, запрос обработал 2.31 миллиона строк и прочитал 13.66 МБ данных. Это произошло потому, что на этапе анализа индекса ClickHouse выбрал 282 гранулы для обработки, каждая из которых содержит 8192 строки, в сумме примерно 2.31 миллиона строк:
Независимо от настроенного значения max_threads
, ClickHouse выделяет дополнительные параллельные обработчики только тогда, когда существует достаточно данных, чтобы это оправдать. "max" в max_threads
относится к верхнему пределу, а не к гарантированному количеству используемых потоков.
Что "достаточно данных" подразумевает, определяется в первую очередь двумя настройками, которые определяют минимальное количество строк (по умолчанию 163840) и минимальное количество байт (по умолчанию 2097152), которые каждый обработчик должен обрабатывать:
Для кластеров без общих ресурсов:
Для кластеров с общим хранилищем (например, ClickHouse Cloud):
- merge_tree_min_rows_for_concurrent_read_for_remote_filesystem
- merge_tree_min_bytes_for_concurrent_read_for_remote_filesystem
Кроме того, существует жесткий нижний предел для размера задач чтения, контролируемый:
Мы не рекомендуем изменять эти настройки в производственной среде. Они показываются здесь исключительно для иллюстрации того, почему max_threads
не всегда определяет фактический уровень параллелизма.
В демонстрационных целях давайте исследуем физический план, переопределив эти настройки, чтобы принудительно максимизировать степень параллелизма:
Теперь ClickHouse использует 59 параллельных потоков для сканирования данных, полностью соблюдая настроенное значение max_threads
.
Это демонстрирует, что для запросов по небольшим наборам данных ClickHouse намеренно ограничивает параллелизм. Используйте переопределения настроек только для тестирования — не в производственной среде — так как они могут привести к неэффективному выполнению или конфликту ресурсов.
Ключевые выводы
- ClickHouse параллелизует запросы, используя обработчики, связанные с
max_threads
. - Фактическое количество обработчиков зависит от объема данных, выбранного для обработки.
- Используйте
EXPLAIN PIPELINE
и журналы трассировки для анализа использования обработчиков.
Где найти дополнительную информацию
Если вы хотите углубиться в то, как ClickHouse выполняет запросы в параллельном режиме и как он достигает высокой производительности в больших масштабах, изучите следующие ресурсы:
-
Слой обработки запросов – статья VLDB 2024 (веб-редакция) - Подробный анализ внутренней модели выполнения ClickHouse, включая планирование, конвейеризацию и проектирование операторов.
-
Объяснение частичных состояний агрегации - Техническое глубокое погружение в то, как частичные состояния агрегации позволяют эффективное параллельное выполнение запросов через обработчики.
-
Видеоурок, подробно объясняющий все этапы обработки запросов ClickHouse: