Анализ запросов

Чтобы проанализировать, как ADQM преобразует и выполняет запросы, используйте выражение EXPLAIN. Базовый синтаксис:

EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE] [<setting_name> = <setting_value>, ...]
<query_to_analyze>
[FORMAT <output_format>]

где:

  • EXPLAIN [<explain_type>] [<explain_settings>] — тип анализатора EXPLAIN (см. таблицу ниже) и соответствующие ему дополнительные настройки;

  • <query_to_analyze> — анализируемый запрос;

  • FORMAT <output_format> — формат вывода результата анализа.

Типы анализатора EXPLAIN
Тип EXPLAIN Результат выполнения EXPLAIN

AST

Абстрактное синтаксическое дерево запроса

SYNTAX

Оптимизированная версия синтаксиса запроса, на которую ADQM заменит исходный текст запроса при его выполнении

QUERY TREE

Дерево запроса после его оптимизации

PLAN

План выполнения запроса. Этот тип EXPLAIN используется по умолчанию

PIPELINE

Конвейер выполнения запроса

ESTIMATE

Оценка числа строк, засечек (marks) и кусков данных, которые будут обработаны при выполнении запроса

TABLE OVERRIDE

Предварительная проверка настроек переопределения схемы таблицы для движка баз данных MaterializedMySQL

EXPLAIN AST

Запрос EXPLAIN AST выводит абстрактное синтаксическое дерево (Abstract Syntax Tree, AST) анализируемого запроса. Поддерживается для всех типов запросов, не только SELECT.

Пример

EXPLAIN AST SELECT sum(number) AS test_sums FROM numbers(5) GROUP BY number % 2;
┌─explain─────────────────────────────────────────┐
│ SelectWithUnionQuery (children 1)               │
│  ExpressionList (children 1)                    │
│   SelectQuery (children 3)                      │
│    ExpressionList (children 1)                  │
│     Function sum (alias test_sums) (children 1) │
│      ExpressionList (children 1)                │
│       Identifier number                         │
│    TablesInSelectQuery (children 1)             │
│     TablesInSelectQueryElement (children 1)     │
│      TableExpression (children 1)               │
│       Function numbers (children 1)             │
│        ExpressionList (children 1)              │
│         Literal UInt64_5                        │
│    ExpressionList (children 1)                  │
│     Function modulo (children 1)                │
│      ExpressionList (children 2)                │
│       Identifier number                         │
│       Literal UInt64_2                          │
└─────────────────────────────────────────────────┘

Параметры

Для запроса EXPLAIN AST можно использовать опцию graph, чтобы получить абстрактное синтаксическое дерево в виде графа, описанного на языке DOT. Полученный синтаксис можно загрузить в специальное приложение для отрисовки графов (например, Graphviz — пакет утилит с открытым исходным кодом для автоматической визуализации графов).

Пример построения абстрактного синтаксического дерева в виде графа:

EXPLAIN AST graph=1 SELECT sum(number) AS test_sums FROM numbers(5) GROUP BY number % 2;
┌─explain────────────────────────────────────────────────────────────────────┐
│ digraph {                                                                  │
│     rankdir="UD";                                                          │
│     n140208872268568[label="SelectWithUnionQuery (children 1)"];           │
│     n140208877065784[label="ExpressionList (children 1)"];                 │
│     n140205632168472[label="SelectQuery (children 3)"];                    │
│     n140206404082776[label="ExpressionList (children 1)"];                 │
│     n140206025898776[label="Function sum (alias test_sums) (children 1)"]; │
│     n140206026032952[label="ExpressionList (children 1)"];                 │
│     n140205801742360[label="Identifier number"];                           │
│     ...                                                                    │
│ }                                                                          │
└────────────────────────────────────────────────────────────────────────────┘

EXPLAIN SYNTAX

При выполнении запросов ADQM может автоматически переписывать их синтаксис для улучшения производительности. Запрос EXPLAIN SYNTAX позволяет посмотреть применяемые ADQM синтаксические оптимизации — в дальнейшем их можно использовать, чтобы вручную оптимизировать сложные запросы для повышения скорости их выполнения.

Пример

  1. Создайте таблицу типа MergeTree и добавьте в нее 1 миллион строк, указав ключ партиционирования так, чтобы данные поделились на 10 партиций:

    CREATE TABLE test_table (value UInt64)
    ENGINE = MergeTree
    PARTITION BY value % 10
    ORDER BY value AS
    SELECT number
    FROM numbers(1000000);
  2. Выполните запрос, который вычисляет среднее арифметическое по столбцу value для первой партиции:

    SELECT avg(value)
    FROM (SELECT value FROM test_table)
    WHERE (value % 10) = 1;
  3. В результате выполнения запроса видно, что ADQM обработал только 100 тысяч строк, а не 1 миллион:

    ┌─avg(value)─┐
    │     499996 │
    └────────────┘
    
    1 row in set. Elapsed: 0.006 sec. Processed 100.00 thousand rows, 800.00 KB (16.40 million rows/s., 131.16 MB/s.)
  4. Выполните анализ EXPLAIN SYNTAX для приведенного выше запроса:

    EXPLAIN SYNTAX
    SELECT avg(value)
    FROM (SELECT value FROM test_table)
    WHERE (value % 10) = 1;

    В результате этого анализа видно, что ADQM автоматически оптимизировал запрос, переместив фильтр WHERE внутрь подзапроса — поэтому достаточно обработать только 1/10 часть данных таблицы:

    ┌─explain────────────────────┐
    │ SELECT avg(value)          │
    │ FROM                       │
    │ (                          │
    │     SELECT value           │
    │     FROM test_table        │
    │     WHERE (value % 10) = 1 │
    │ )                          │
    │ WHERE (value % 10) = 1     │
    └────────────────────────────┘

    Таким образом можно использовать этот способ оптимизации в более сложных запросах, сразу указывая необходимые фильтры в подзапросах.

EXPLAIN QUERY TREE

Запрос EXPLAIN QUERY TREE создает дамп дерева запроса. Этот тип анализатора находится в стадии разработки, но его можно использовать в экспериментальном режиме. Для этого перед выполнением запроса необходимо включить опцию: SET allow_experimental_analyzer = 1.

Создание таблицы для выполнения приведенных ниже примеров

 
В примерах запросов EXPLAIN QUERY TREE, приведенных ниже, используется тестовая таблица с данными о просмотрах страниц, сгенерированными случайным образом. Чтобы воспроизвести эти примеры, создайте предварительно таблицу page_views:

CREATE TABLE page_views (time DateTime, page_name String, time_on_page UInt64)
ENGINE = MergeTree
ORDER BY time;
INSERT INTO page_views
SELECT
    toDate('2023-01-01 00:00:00') + (rand() % 182) AS time,
    ['landing', 'products', 'docs', 'about'][toInt32(randBinomial(4, 0.01)) + 1],
    rand() % 15
FROM numbers(1000000);

Пример

SET allow_experimental_analyzer = 1;
EXPLAIN QUERY TREE
SELECT page_name, toDate(toStartOfMonth(time)) AS month, count() AS page_view_count
FROM page_views
WHERE page_name = 'landing'
GROUP BY page_name, month
ORDER BY page_name, month
LIMIT 3;
┌─explain───────────────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY id: 0                                                                                               │
│   PROJECTION COLUMNS                                                                                      │
│     page_name String                                                                                      │
│     month Date                                                                                            │
│     page_view_count UInt64                                                                                │
│   PROJECTION                                                                                              │
│     LIST id: 1, nodes: 3                                                                                  │
│       COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3                             │
│       FUNCTION id: 4, function_name: toDate, function_type: ordinary, result_type: Date                   │
│         ARGUMENTS                                                                                         │
│           LIST id: 5, nodes: 1                                                                            │
│             FUNCTION id: 6, function_name: toStartOfMonth, function_type: ordinary, result_type: Date     │
│               ARGUMENTS                                                                                   │
│                 LIST id: 7, nodes: 1                                                                      │
│                   COLUMN id: 8, column_name: time, result_type: DateTime, source_id: 3                    │
│       FUNCTION id: 9, function_name: count, function_type: aggregate, result_type: UInt64                 │
│   JOIN TREE                                                                                               │
│     TABLE id: 3, table_name: default.page_views                                                           │
│   WHERE                                                                                                   │
│     FUNCTION id: 10, function_name: equals, function_type: ordinary, result_type: UInt8                   │
│       ARGUMENTS                                                                                           │
│         LIST id: 11, nodes: 2                                                                             │
│           COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3                         │
│           CONSTANT id: 12, constant_value: 'landing', constant_value_type: String                         │
│   GROUP BY                                                                                                │
│     LIST id: 13, nodes: 2                                                                                 │
│       COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3                             │
│       FUNCTION id: 4, function_name: toDate, function_type: ordinary, result_type: Date                   │
│         ARGUMENTS                                                                                         │
│           LIST id: 5, nodes: 1                                                                            │
│             FUNCTION id: 6, function_name: toStartOfMonth, function_type: ordinary, result_type: Date     │
│               ARGUMENTS                                                                                   │
│                 LIST id: 7, nodes: 1                                                                      │
│                   COLUMN id: 8, column_name: time, result_type: DateTime, source_id: 3                    │
│   ORDER BY                                                                                                │
│     LIST id: 14, nodes: 2                                                                                 │
│       SORT id: 15, sort_direction: ASCENDING, with_fill: 0                                                │
│         EXPRESSION                                                                                        │
│           COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3                         │
│       SORT id: 16, sort_direction: ASCENDING, with_fill: 0                                                │
│         EXPRESSION                                                                                        │
│           FUNCTION id: 4, function_name: toDate, function_type: ordinary, result_type: Date               │
│             ARGUMENTS                                                                                     │
│               LIST id: 5, nodes: 1                                                                        │
│                 FUNCTION id: 6, function_name: toStartOfMonth, function_type: ordinary, result_type: Date │
│                   ARGUMENTS                                                                               │
│                     LIST id: 7, nodes: 1                                                                  │
│                       COLUMN id: 8, column_name: time, result_type: DateTime, source_id: 3                │
│   LIMIT                                                                                                   │
│     CONSTANT id: 17, constant_value: UInt64_3, constant_value_type: UInt64                                │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘
РЕКОМЕНДАЦИЯ
Чтобы ускорить выполнение запросов, в которых вычисляются агрегатные значения (как, например, в запросе выше вычисляется количество просмотров страниц по месяцам), можно использовать материализованные представления, автоматически вычисляющие промежуточные агрегатные состояния данных — см. пример в статье Материализованные представления.

Параметры

Вывод результата запроса EXPLAIN QUERY TREE можно настроить с помощью следующих параметров (если значение параметра 1 — соответствующая настройка включена, если 0 — выключена):

  • run_passes — указывает, выполнять ли все проходы дерева запроса перед созданием дампа дерева запроса (значение по умолчанию — 1);

  • passes — определяет, сколько проходов дерева запроса выполнять (значение по умолчанию — -1, выполняются все проходы);

  • dump_passes — указывает, выводить ли информацию о выполненных проходах перед созданием дампа дерева запроса (значение по умолчанию — 0).

Пример

 
Cледующий анализ выполняет три прохода дерева запроса и выводит информацию о них перед дампом дерева запроса:

EXPLAIN QUERY TREE passes=3, dump_passes=1
SELECT page_name, toDate(toStartOfMonth(time)) AS month, count() AS page_view_count
FROM page_views
WHERE page_name = 'landing'
GROUP BY page_name, month
ORDER BY page_name, month
LIMIT 3
FORMAT TSVRaw;
Pass 1 QueryAnalysis - Resolve type for each query expression. Replace identifiers, matchers with query expressions. Perform constant folding. Evaluate scalar subqueries.
Pass 2 FunctionToSubcolumns - Rewrite function to subcolumns, for example tupleElement(column, subcolumn) into column.subcolumn
Pass 3 CountDistinct - Optimize single countDistinct into count over subquery
QUERY id: 0
  PROJECTION COLUMNS
    page_name String
    month Date
    page_view_count UInt64
  PROJECTION
    LIST id: 1, nodes: 3
      COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3
      FUNCTION id: 4, function_name: toDate, function_type: ordinary, result_type: Date
        ARGUMENTS
          LIST id: 5, nodes: 1
            FUNCTION id: 6, function_name: toStartOfMonth, function_type: ordinary, result_type: Date
              ARGUMENTS
                LIST id: 7, nodes: 1
                  COLUMN id: 8, column_name: time, result_type: DateTime, source_id: 3
      FUNCTION id: 9, function_name: count, function_type: aggregate, result_type: UInt64
  JOIN TREE
    TABLE id: 3, table_name: default.page_views
  WHERE
    FUNCTION id: 10, function_name: equals, function_type: ordinary, result_type: UInt8
      ARGUMENTS
        LIST id: 11, nodes: 2
          COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3
          CONSTANT id: 12, constant_value: 'landing', constant_value_type: String
  GROUP BY
    LIST id: 13, nodes: 2
      COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3
      FUNCTION id: 4, function_name: toDate, function_type: ordinary, result_type: Date
        ARGUMENTS
          LIST id: 5, nodes: 1
            FUNCTION id: 6, function_name: toStartOfMonth, function_type: ordinary, result_type: Date
              ARGUMENTS
                LIST id: 7, nodes: 1
                  COLUMN id: 8, column_name: time, result_type: DateTime, source_id: 3
  ORDER BY
    LIST id: 14, nodes: 2
      SORT id: 15, sort_direction: ASCENDING, with_fill: 0
        EXPRESSION
          COLUMN id: 2, column_name: page_name, result_type: String, source_id: 3
      SORT id: 16, sort_direction: ASCENDING, with_fill: 0
        EXPRESSION
          FUNCTION id: 4, function_name: toDate, function_type: ordinary, result_type: Date
            ARGUMENTS
              LIST id: 5, nodes: 1
                FUNCTION id: 6, function_name: toStartOfMonth, function_type: ordinary, result_type: Date
                  ARGUMENTS
                    LIST id: 7, nodes: 1
                      COLUMN id: 8, column_name: time, result_type: DateTime, source_id: 3
  LIMIT
    CONSTANT id: 17, constant_value: UInt64_3, constant_value_type: UInt64

EXPLAIN PLAN

Запрос EXPLAIN PLAN выводит план выполнения анализируемого запроса в виде дерева, которое показывает в каком порядке будут выполняться выражения запроса. Этапы плана выполнения запроса читаются снизу вверх. Этот тип анализатора используется по умолчанию, если не указан другой тип EXPLAIN.

Пример

Следующий анализ показывает, что для выполнения запроса SELECT ADQM сначала считывает данные из таблицы (ReadFromStorage), затем фильтрует их (Filter) и после этого агрегирует (Aggregating).

EXPLAIN SELECT sum(number) AS test_sums FROM numbers(10000) WHERE number > 100 GROUP BY number % 2;
┌─explain─────────────────────────────────────┐
│ Expression ((Projection + Before ORDER BY)) │
│   Aggregating                               │
│     Expression (Before GROUP BY)            │
│       Filter (WHERE)                        │
│         ReadFromStorage (SystemNumbers)     │
└─────────────────────────────────────────────┘

Параметры

В запросах EXPLAIN PLAN можно использовать следующие параметры, чтобы указать какая информация должна включаться в результат выполняемого анализа (если значение параметра 1 — соответствующая настройка включена, если 0 — выключена):

  • header — в план выполнения запроса добавляется ключ Header в виде массива столбцов (выводится название и тип столбца), значения которых вычисляются на каждом шаге.

    Пример
    EXPLAIN header = 0 (по умолчанию) EXPLAIN header = 1
    EXPLAIN
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY)) │
    │   Aggregating                               │
    │     Expression (Before GROUP BY)            │
    │       ReadFromStorage (SystemNumbers)       │
    └─────────────────────────────────────────────┘
    EXPLAIN header = 1
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY)) │
    │ Header: test_sums UInt64                    │
    │   Aggregating                               │
    │   Header: modulo(number, 2) UInt8           │
    │           sum(number) UInt64                │
    │     Expression (Before GROUP BY)            │
    │     Header: number UInt64                   │
    │             modulo(number, 2) UInt8         │
    │       ReadFromStorage (SystemNumbers)       │
    │       Header: number UInt64                 │
    └─────────────────────────────────────────────┘
  • description — план выполнения запроса включает описание каждого шага.

    Пример
    EXPLAIN description = 0 EXPLAIN description = 1 (по умолчанию)
    EXPLAIN description = 0
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain───────────────┐
    │ Expression            │
    │   Aggregating         │
    │     Expression        │
    │       ReadFromStorage │
    └───────────────────────┘
    EXPLAIN
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY)) │
    │   Aggregating                               │
    │     Expression (Before GROUP BY)            │
    │       ReadFromStorage (SystemNumbers)       │
    └─────────────────────────────────────────────┘
  • indexes — в план выполнения запроса добавляется ключ Indexes, который содержит массив используемых индексов. Для каждого индекса выводится ключ Type (возможные значения: MinMax, Partition, PrimaryKey, Skip) и дополнительные ключи:

    • Name — имя индекса (выводится только для индекса Skip);

    • Keys — массив столбцов, используемых индексом;

    • Condition — используемое условие;

    • Description — описание индекса (выводится только для индекса Skip);

    • Parts — количество кусков данных до/после применения индекса;

    • Granules — количество гранул до/после применения индекса.

    Этот параметр поддерживается только для анализа запросов, обращающихся к таблицам семейства MergeTree.

    Пример
    EXPLAIN actions = 0 (default) EXPLAIN actions = 1
    EXPLAIN indexes = 0
    SELECT sum(value)
    FROM test_table
    WHERE (value % 10) = 1;
    ┌─explain────────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY))    │
    │   Aggregating                                  │
    │     Expression (Before GROUP BY)               │
    │       Filter (WHERE)                           │
    │         ReadFromMergeTree (default.test_table) │
    └────────────────────────────────────────────────┘
    EXPLAIN indexes = 1
    SELECT sum(value)
    FROM test_table
    WHERE (value % 10) = 1;
    ┌─explain────────────────────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY))                │
    │   Aggregating                                              │
    │     Expression (Before GROUP BY)                           │
    │       Filter (WHERE)                                       │
    │         ReadFromMergeTree (default.test_table)             │
    │         Indexes:                                           │
    │           MinMax                                           │
    │             Condition: true                                │
    │             Parts: 10/10                                   │
    │             Granules: 120/120                              │
    │           Partition                                        │
    │             Keys:                                          │
    │               moduloLegacy(value, 10)                      │
    │             Condition: (moduloLegacy(value, 10) in [1, 1]) │
    │             Parts: 1/10                                    │
    │             Granules: 12/120                               │
    │           PrimaryKey                                       │
    │             Condition: true                                │
    │             Parts: 1/1                                     │
    │             Granules: 12/12                                │
    └────────────────────────────────────────────────────────────┘
  • actions — в план выполнения запроса добавляется подробная информация о действиях, выполняемых на каждом шаге (ключ Actions).

    Пример
    EXPLAIN actions = 0 (по умолчанию) EXPLAIN actions = 1
    EXPLAIN
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY)) │
    │   Aggregating                               │
    │     Expression (Before GROUP BY)            │
    │       ReadFromStorage (SystemNumbers)       │
    └─────────────────────────────────────────────┘
    EXPLAIN actions = 1
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY))                                     │
    │ Actions: INPUT : 0 -> sum(number) UInt64 : 0                                    │
    │          ALIAS sum(number) :: 0 -> test_sums UInt64 : 1                         │
    │ Positions: 1                                                                    │
    │   Aggregating                                                                   │
    │   Keys: modulo(number, 2)                                                       │
    │   Aggregates:                                                                   │
    │       sum(number)                                                               │
    │         Function: sum(UInt64) → UInt64                                          │
    │         Arguments: number                                                       │
    │   Skip merging: 0                                                               │
    │     Expression (Before GROUP BY)                                                │
    │     Actions: INPUT : 0 -> number UInt64 : 0                                     │
    │              COLUMN Const(UInt8) -> 2 UInt8 : 1                                 │
    │              FUNCTION modulo(number : 0, 2 :: 1) -> modulo(number, 2) UInt8 : 2 │
    │     Positions: 0 2                                                              │
    │       ReadFromStorage (SystemNumbers)                                           │
    └─────────────────────────────────────────────────────────────────────────────────┘
  • json — план выполнения запроса выводится в формате JSON. Каждый узел — это словарь, в котором всегда есть ключи Node Type (название шага) и Plans (массив с описаниями дочерних шагов). Другие дополнительные ключи могут быть добавлены в зависимости от типа узла и настроек.

    Чтобы избежать ненужного экранирования, рекомендуется использовать формат TSVRaw для вывода результата анализа.

    Пример
    EXPLAIN json = 0 (по умолчанию) EXPLAIN json = 1
    EXPLAIN
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────┐
    │ Expression ((Projection + Before ORDER BY)) │
    │   Aggregating                               │
    │     Expression (Before GROUP BY)            │
    │       ReadFromStorage (SystemNumbers)       │
    └─────────────────────────────────────────────┘
    EXPLAIN json = 1
    SELECT sum(number) AS test_sums
    FROM numbers(5) GROUP BY number % 2 FORMAT TSVRaw;
    [
      {
        "Plan": {
          "Node Type": "Expression",
          "Description": "(Projection + Before ORDER BY)",
          "Plans": [
            {
              "Node Type": "Aggregating",
              "Plans": [
                {
                  "Node Type": "Expression",
                  "Description": "Before GROUP BY",
                  "Plans": [
                    {
                      "Node Type": "ReadFromStorage",
                      "Description": "SystemNumbers"
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    ]

EXPLAIN PIPELINE

Запрос EXPLAIN PIPELINE выводит конвейер выполнения запроса, который показывает сколько параллельных потоков используется для выполнения каждого шага плана запроса.

Пример

EXPLAIN PIPELINE SELECT sum(number) AS test_sums FROM numbers(5) GROUP BY number % 2;
┌─explain───────────────────┐
│ (Expression)              │
│ ExpressionTransform × 8   │
│   (Aggregating)           │
│   Resize 1 → 8            │
│     AggregatingTransform  │
│       (Expression)        │
│       ExpressionTransform │
│         (ReadFromStorage) │
│         Limit             │
│           Numbers 0 → 1   │
└───────────────────────────┘

Параметры

Вывод результата выполнения запроса EXPLAIN PIPELINE можно настраивать с помощью следующих параметров (если значение параметра 1 — настройка включена, если 0 — выключена):

  • header — выводится заголовок для каждого выходного порта (значение по умолчанию — 0).

    Пример
    EXPLAIN PIPELINE header = 1
    SELECT sum(number) AS test_sums FROM numbers(5) GROUP BY number % 2;
    ┌─explain─────────────────────────────────────────────────────────────────────────┐
    │ (Expression)                                                                    │
    │ ExpressionTransform × 8                                                         │
    │ Header: test_sums UInt64: test_sums UInt64 UInt64(size = 0)                     │
    │   (Aggregating)                                                                 │
    │   Resize 1 → 8                                                                  │
    │   Header × 8 : modulo(number, 2) UInt8: modulo(number, 2) UInt8 UInt8(size = 0) │
    │                 sum(number) UInt64: sum(number) UInt64 UInt64(size = 0)         │
    │     AggregatingTransform                                                        │
    │     Header: modulo(number, 2) UInt8: modulo(number, 2) UInt8 UInt8(size = 0)    │
    │             sum(number) UInt64: sum(number) UInt64 UInt64(size = 0)             │
    │       (Expression)                                                              │
    │       ExpressionTransform                                                       │
    │       Header: number UInt64: number UInt64 UInt64(size = 0)                     │
    │               modulo(number, 2) UInt8: modulo(number, 2) UInt8 UInt8(size = 0)  │
    │         (ReadFromStorage)                                                       │
    │         Limit                                                                   │
    │         Header: number UInt64: number UInt64 UInt64(size = 0)                   │
    │           Numbers 0 → 1                                                         │
    │           Header: number UInt64: number UInt64 UInt64(size = 0)                 │
    └─────────────────────────────────────────────────────────────────────────────────┘
  • graph — конвейер выполнения запроса выводится в виде описанного на языке DOT графа (значение по умолчанию — 0). Полученный синтаксис можно загрузить в приложение для визуализации графов (например, Graphviz).

    Пример
    EXPLAIN PIPELINE graph = 1
    SELECT sum(number) AS test_sums FROM numbers(5) GROUP BY number % 2 FORMAT TSV;
    digraph
    {
      rankdir="LR";
      { node [shape = rect]
        subgraph cluster_0 {
          label ="Expression";
          style=filled;
          color=lightgrey;
          node [style=filled,color=white];
          { rank = same;
            n6 [label="ExpressionTransform × 8"];
          }
        }
        subgraph cluster_1 {
          label ="ReadFromStorage";
          style=filled;
          color=lightgrey;
          node [style=filled,color=white];
          { rank = same;
            n2 [label="Limit"];
            n1 [label="Numbers"];
          }
        }
        subgraph cluster_2 {
          label ="Expression";
          style=filled;
          color=lightgrey;
          node [style=filled,color=white];
          { rank = same;
            n3 [label="ExpressionTransform"];
          }
        }
        subgraph cluster_3 {
          label ="Aggregating";
          style=filled;
          color=lightgrey;
          node [style=filled,color=white];
          { rank = same;
            n4 [label="AggregatingTransform"];
            n5 [label="Resize"];
          }
        }
      }
      n2 -> n3 [label=""];
      n1 -> n2 [label=""];
      n3 -> n4 [label=""];
      n4 -> n5 [label=""];
      n5 -> n6 [label="× 8"];
    }
  • compact — если включена настройка graph, граф выводится в компактном режиме (значение по умолчанию — 1).

    Пример
    EXPLAIN PIPELINE graph = 1, compact = 0
    SELECT sum(number) AS test_sums FROM numbers(5) GROUP BY number % 2 FORMAT TSV;
    digraph
    {
      rankdir="LR";
      { node [shape = rect]
        n140206024061464[label="Numbers"];
        n140205465170584[label="Limit"];
        n140205620507928[label="ExpressionTransform"];
        n140205801688600[label="AggregatingTransform"];
        n140205649781464[label="Resize"];
        n140205630520728[label="ExpressionTransform"];
        n140205788048536[label="ExpressionTransform"];
        n140205620512152[label="ExpressionTransform"];
        n140206023882392[label="ExpressionTransform"];
        n140206024060696[label="ExpressionTransform"];
        n140205620509080[label="ExpressionTransform"];
        n140205717371544[label="ExpressionTransform"];
        n140206023882008[label="ExpressionTransform"];
      }
      n140206024061464 -> n140205465170584;
      n140205465170584 -> n140205620507928;
      n140205620507928 -> n140205801688600;
      n140205801688600 -> n140205649781464;
      n140205649781464 -> n140205630520728;
      n140205649781464 -> n140205788048536;
      n140205649781464 -> n140205620512152;
      n140205649781464 -> n140206023882392;
      n140205649781464 -> n140206024060696;
      n140205649781464 -> n140205620509080;
      n140205649781464 -> n140205717371544;
      n140205649781464 -> n140206023882008;
    }

EXPLAIN ESTIMATE

Анализ с помощью запроса EXPLAIN ESTIMATE показывает предполагаемое количество строк, засечек и кусков данных, которые необходимо считать из таблицы при обработке запроса. Этот тип анализа можно применять только к запросам, обращающимся к таблицам семейства MergeTree.

Пример

  1. Создайте таблицу MergeTree, используя специальные опции, чтобы настроить деление кусков данных на гранулы:

    • index_granularity — задает максимальное количество строк данных между засечками индекса;

    • write_final_mark — указывает, записывать ли последнюю засечку индекса в конце куска данных, указывающую на последний байт.

    CREATE TABLE test_table_estimate (i Int64) ENGINE = MergeTree ORDER BY i SETTINGS index_granularity = 16, write_final_mark = 0;
  2. Вставьте данные в таблицу, повторив следующую команду три раза:

    INSERT INTO test_table_estimate SELECT number FROM numbers(128);
  3. Выполните анализ запроса для чтения данных из таблицы:

    EXPLAIN ESTIMATE SELECT * FROM test_table_estimate;

    Результат анализа:

    ┌─database─┬─table───────────────┬─parts─┬─rows─┬─marks─┐
    │ default  │ test_table_estimate │     3 │  384 │    24 │
    └──────────┴─────────────────────┴───────┴──────┴───────┘

EXPLAIN TABLE OVERRIDE

Запрос EXPLAIN TABLE OVERRIDE позволяет предварительно проверить корректность конфигураций для переопределения схемы таблицы, которые можно указать при создании базы данных типа MaterializedMySQL (см. Table Overrides). Если переопределение схемы таблицы может привести к какой-либо ошибке, будет выведено соответствующее сообщение (например, при использовании столбцов типа Nullable для выражений PARTITION BY и ORDER BY, для которых это не рекомендуется).

Для выполнения запроса EXPLAIN TABLE OVERRIDE подключение к таблице на удаленном сервере MySQL осуществляется через табличную функцию mysql.

Пример

  1. На удаленном сервере MySQL создайте таблицу в базе данных mysql_db:

    CREATE TABLE mysql_table (id INT PRIMARY KEY, date DATETIME DEFAULT now());
  2. В ADQM выполните следующий запрос, чтобы проверить корректность партиционирование таблицы по столбцу date (замените <host>:<port> на адрес сервера MySQL, а <user_name> и <user_password> на имя пользователя и пароль для доступа к базе данных MySQL):

    EXPLAIN TABLE OVERRIDE mysql('<host>:<port>', 'mysql_db', 'mysql_table', '<user_name>', '<user_password>')
    PARTITION BY toYYYYMM(date);

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

    Exception: PARTITION BY override refers to nullable column `date` (use assumeNotNull() if the column does not in fact contain NULL values). (INVALID_TABLE_OVERRIDE)
    ....`
  3. Измените переопределение PARTITION BY в соответствии с указанной рекомендацией:

    EXPLAIN TABLE OVERRIDE mysql('<host>:<port>', 'mysql_db', 'mysql_table', 'root', 'my-secret-pw')
    PARTITION BY toYYYYMM(assumeNotNull(date));

    Вывод:

    ┌─explain──────────────────────────────────────────────┐
    │ PARTITION BY uses columns: `date` Nullable(DateTime) │
    └──────────────────────────────────────────────────────┘
Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней