EXPLAIN PIPELINE: читаем DAG процессоров
ClickHouse предоставляет шесть вариантов EXPLAIN — от разбора синтаксиса до оценки количества строк. Каждый отвечает на свой вопрос. EXPLAIN PIPELINE — самый мощный: показывает физический план выполнения как DAG (directed acyclic graph) процессоров с указанием параллелизма.
6 вариантов EXPLAIN
| Вариант | Что показывает | Ключевые настройки | Когда использовать |
|---|---|---|---|
| EXPLAIN AST | Абстрактное синтаксическое дерево | graph (DOT формат) | Понять как ClickHouse распарсил запрос |
| EXPLAIN SYNTAX | Оптимизированный AST | oneline | Увидеть какие оптимизации применены автоматически |
| EXPLAIN QUERY TREE | Дерево запроса (новый анализатор) | run_passes | Отладка трансформаций нового анализатора |
| EXPLAIN PLAN | Логический план выполнения | indexes, json, pretty, distributed | Анализ использования индексов, порядок JOIN |
| EXPLAIN PIPELINE | Физический DAG процессоров | graph (DOT формат) | Анализ параллелизма, поиск bottleneck |
| EXPLAIN ESTIMATE | Оценка строк/marks/parts | — | Быстрая оценка объёма без выполнения запроса |
EXPLAIN PIPELINE: физический план
EXPLAIN PIPELINE показывает цепочку процессоров, через которые пройдут данные. Каждый процессор — это узел в DAG. Между процессорами передаются Block (см. предыдущий урок).
EXPLAIN PIPELINE
SELECT
toStartOfHour(event_time) AS hour,
count() AS events,
uniq(user_id) AS users
FROM events
WHERE event_date = '2024-01-15'
GROUP BY hour
ORDER BY hour
Вывод:
(Expression)
ExpressionTransform
(Sorting)
MergeSortingTransform
(Expression)
ExpressionTransform
(Aggregating)
Resize 4 → 1
AggregatingTransform × 4
(Expression)
ExpressionTransform × 4
(ReadFromMergeTree)
MergeTreeSelect(pool: ReadPoolParallelizeOutput, algorithm: Thread) × 4
Чтение вывода EXPLAIN PIPELINE
Имена процессоров
| Процессор | Назначение |
|---|---|
| MergeTreeSelect / ReadFromMergeTree | Чтение гранул из .bin файлов. Количество потоков определяется max_threads |
| ExpressionTransform | Вычисление выражений: WHERE, проекция столбцов, вычисляемые поля |
| FilterTransform | Фильтрация строк после вычисления условия (PREWHERE/WHERE) |
| AggregatingTransform | Агрегация GROUP BY: строит hash table, накапливает состояния |
| MergingAggregatedTransform | Слияние частичных агрегатов из параллельных потоков |
| MergeSortingTransform | Сортировка ORDER BY (отсутствует при read_in_order) |
| LimitsCheckingTransform | Применение LIMIT |
| Resize | Изменение ширины потока: N потоков к M потокам |
Параллельные инстансы: x N
AggregatingTransform x 4 означает 4 параллельных инстанса этого процессора. Каждый обрабатывает свою порцию Block. Число определяется настройкой max_threads (по умолчанию = количество CPU ядер).
Resize: изменение ширины
Resize 4 -> 1 означает схождение 4 параллельных потоков в 1. Это типично перед финальным слиянием агрегатов — MergingAggregatedTransform работает в одном потоке, объединяя результаты четырёх AggregatingTransform.
Resize 1 -> 4 — расхождение: один поток разделяется на 4 для параллельной обработки.
EXPLAIN PLAN: логический план с индексами
EXPLAIN PLAN показывает логический план без деталей параллелизма. Ключевая настройка — indexes=1: показывает какие индексы ClickHouse использует для pruning.
EXPLAIN PLAN indexes = 1
SELECT count() FROM events
WHERE event_date = '2024-01-15' AND status = 'error'
Expression
Aggregating
Expression
ReadFromMergeTree
Indexes:
PrimaryKey
Keys: event_date
Condition: (event_date in ['2024-01-15', '2024-01-15'])
Parts: 3/12
Granules: 45/1200
Skip
Name: idx_status
Type: set(100)
Parts: 2/3
Granules: 8/45
Что видно:
- PrimaryKey отсёк 9 из 12 parts (оставил 3) и 1155 из 1200 гранул (оставил 45)
- Skip index idx_status (set) дополнительно отсёк 1 part и 37 гранул
- Финально ClickHouse прочитает 8 гранул из 1200 — менее 1%
Всегда проверяйте skip-индексы через EXPLAIN PLAN indexes=1. Если индекс не отсекает гранулы — он бесполезен и только занимает место.
EXPLAIN PLAN indexes = 1
SELECT * FROM your_table WHERE your_filter_conditionEXPLAIN ESTIMATE: быстрая оценка
EXPLAIN ESTIMATE не выполняет запрос — только оценивает объём данных по метаданным parts:
EXPLAIN ESTIMATE
SELECT * FROM events WHERE event_date = '2024-01-15'
┌─database─┬─table──┬─parts─┬─rows──────┬─marks─┐
│ default │ events │ 3 │ 2457600 │ 300 │
└──────────┴────────┴───────┴───────────┴───────┘
Это мгновенная операция: ClickHouse читает только метаданные (system.parts), не обращаясь к данным. Полезно для:
- Быстрой оценки “сколько данных затронет запрос”
- Проверки partition pruning до выполнения тяжёлого запроса
- Мониторинга роста данных
EXPLAIN AST и EXPLAIN SYNTAX
EXPLAIN AST
Показывает дерево разбора запроса. Полезно для понимания приоритета операций:
EXPLAIN AST
SELECT a + b * c FROM t
EXPLAIN SYNTAX
Показывает запрос после применения автоматических оптимизаций:
EXPLAIN SYNTAX
SELECT * FROM events WHERE event_date = '2024-01-15' AND 1 = 1
Вывод покажет, что 1 = 1 удалено, а * развёрнуто в список столбцов. Если optimize_move_to_prewhere активен, WHERE может быть заменён на PREWHERE.
EXPLAIN QUERY TREE
Доступен с новым анализатором (enable_analyzer = 1, по умолчанию с ClickHouse 24.3). Показывает промежуточное представление запроса после трансформаций анализатора. Полезно для отладки:
EXPLAIN QUERY TREE run_passes = 1
SELECT count() FROM events WHERE event_date = '2024-01-15'
Практические рецепты
Найти bottleneck в запросе
-- 1. Посмотреть pipeline
EXPLAIN PIPELINE
SELECT ... FROM ... WHERE ... GROUP BY ... ORDER BY ...
-- 2. Искать Resize N → 1 (сужение параллелизма)
-- 3. Искать MergeSortingTransform (можно ли избежать через ORDER BY = table ORDER BY?)
-- 4. Если AggregatingTransform x 1 -- проверить max_threads
Проверить использование индексов
EXPLAIN PLAN indexes = 1
SELECT ... FROM ... WHERE ...
-- Если Parts: 12/12 -- primary key не помогает
-- Если Skip Parts: 10/10 -- skip index не помогает
-- Оба случая = нужно пересмотреть ORDER BY или skip index
Оценить объём до выполнения
EXPLAIN ESTIMATE
SELECT ... FROM ... WHERE ...
-- rows > 100M? Добавьте фильтры или используйте SAMPLE
Ключевые выводы
- 6 вариантов EXPLAIN — каждый для своей задачи. PIPELINE для параллелизма, PLAN indexes=1 для индексов, ESTIMATE для быстрой оценки.
- x N в PIPELINE означает N параллельных инстансов процессора. Определяется max_threads.
- Resize показывает изменение ширины потока: 4 к 1 = слияние, 1 к 4 = распараллеливание.
- EXPLAIN PLAN indexes=1 — главный инструмент проверки эффективности primary key и skip-индексов. Если индекс не отсекает гранулы — он бесполезен.
- EXPLAIN ESTIMATE — мгновенная оценка объёма данных по метаданным, без обращения к данным.