Learning Platform
Глоссарий Troubleshooting
Урок 07.02 · 30 мин
Средний
EXPLAINPIPELINEPLANESTIMATEASTSYNTAXQUERY TREEProcessor DAGParallelism

EXPLAIN PIPELINE: читаем DAG процессоров

ClickHouse предоставляет шесть вариантов EXPLAIN — от разбора синтаксиса до оценки количества строк. Каждый отвечает на свой вопрос. EXPLAIN PIPELINE — самый мощный: показывает физический план выполнения как DAG (directed acyclic graph) процессоров с указанием параллелизма.


6 вариантов EXPLAIN

ВариантЧто показываетКлючевые настройкиКогда использовать
EXPLAIN ASTАбстрактное синтаксическое деревоgraph (DOT формат)Понять как ClickHouse распарсил запрос
EXPLAIN SYNTAXОптимизированный ASTonelineУвидеть какие оптимизации применены автоматически
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
Процессорный DAG запроса ClickHouse
ReadFromMergeTree x 4ReadFromMergeTree x 4: первый процессор в DAG. Читает данные из MergeTree партов параллельно (max_threads потоков, по умолчанию = число ядер). Перед чтением: primary.idx pruning отсекает нерелевантные гранулы за O(log N), skip-индексы (minmax, set, bloom_filter) дополнительно фильтруют. Каждый поток читает свой диапазон гранул — данные не дублируются между потоками.
ExpressionTransform x 4ExpressionTransform x 4: вычисляет выражения WHERE/PREWHERE на колоночных блоках. PREWHERE (ClickHouse-специфичный) читает сначала только колонки фильтра, отсекает строки, затем дочитывает остальные колонки -- экономит I/O при низкой селективности. WHERE вычисляется после полного чтения блока. Каждый поток обрабатывает свои блоки независимо.
AggregatingTransform x 4AggregatingTransform x 4: каждый поток строит свою хеш-таблицу агрегации на колоночных блоках. Ключ -- GROUP BY колонки, значения -- промежуточные состояния агрегатных функций (State). При превышении max_bytes_before_external_group_by данные сбрасываются на диск (external aggregation fallback). Параллельность = max_threads.
MergingAggregatedTransform x 1MergingAggregatedTransform x 1: единственный процессор, объединяющий частичные агрегаты из всех параллельных потоков в финальный результат. Использует двухуровневое слияние хеш-таблиц: сначала объединяет ключи, затем вызывает Merge для каждой агрегатной функции. Это узкое место (bottleneck) -- один поток, но объём данных уже значительно сокращён.
РезультатФинальные строки клиентуРезультат отправляется клиенту через выбранный протокол: Native (порт 9000) -- бинарный, минимальные накладные расходы, используется clickhouse-client; HTTP (порт 8123) -- текстовый, совместим с curl/JDBC/ODBC. FORMAT (TabSeparated, JSON, CSV, Pretty и др.) определяет сериализацию. LIMIT применяется здесь -- после полной агрегации.

Чтение вывода 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%
TIP

Всегда проверяйте skip-индексы через EXPLAIN PLAN indexes=1. Если индекс не отсекает гранулы — он бесполезен и только занимает место.

EXPLAIN PLAN indexes = 1
SELECT * FROM your_table WHERE your_filter_condition

EXPLAIN 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

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

  1. 6 вариантов EXPLAIN — каждый для своей задачи. PIPELINE для параллелизма, PLAN indexes=1 для индексов, ESTIMATE для быстрой оценки.
  2. x N в PIPELINE означает N параллельных инстансов процессора. Определяется max_threads.
  3. Resize показывает изменение ширины потока: 4 к 1 = слияние, 1 к 4 = распараллеливание.
  4. EXPLAIN PLAN indexes=1 — главный инструмент проверки эффективности primary key и skip-индексов. Если индекс не отсекает гранулы — он бесполезен.
  5. EXPLAIN ESTIMATE — мгновенная оценка объёма данных по метаданным, без обращения к данным.
Executor PostgreSQL: как выполняется план запроса DAG Scheduler в Spark: от действия к стадиям

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. В выводе EXPLAIN PIPELINE вы видите 'AggregatingTransform x 4'. Что это означает?

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

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

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

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