Описание
Оператор EXPLAIN отображает план выполнения заданного запроса в Doris. Оптимизатор запросов Doris стремится создать эффективный план, используя статистические данные, характеристики данных и такие возможности, как HASH JOIN, партиционирование и бакетирование. Однако из-за теоретических и практических ограничений план иногда оказывается недостаточно эффективным.
Для повышения производительности необходимо проанализировать текущий план. В этой статье объясняется, как использовать оператор EXPLAIN для оптимизации.
Синтаксис
{EXPLAIN | DESC} [VERBOSE] <query_block>
Обязательные параметры
<query_block>
Запрос, для которого требуется получить план выполнения.
Необязательные параметры
[VERBOSE]
Наличие VERBOSE определяет, будет ли отображаться подробная информация. При указании VERBOSE выводятся полные сведения, включая подробности о каждом операторе, идентификаторы используемых им кортежей и подробные описания каждого кортежа. Без этого параметра выводится краткая информация.
Результаты выполнения
Основные понятия
Чтобы лучше понимать информацию, отображаемую командой EXPLAIN, рассмотрим несколько основных понятий плана выполнения Doris.
| Название | Пояснение |
|---|
| PLAN | План выполнения. Планировщик преобразует запрос в план выполнения, который затем выполняется движком исполнения. |
| FRAGMENT | Фрагмент выполнения. Поскольку Doris является распределённым движком исполнения, полный план выполнения разделяется на несколько фрагментов, каждый из которых выполняется на одном узле. Таблица FRAGMENT представляет полный фрагмент выполнения для одного узла. Несколько FRAGMENTS вместе образуют полный PLAN. |
| PLAN NODE | Оператор. Наименьшая единица плана выполнения. FRAGMENT состоит из нескольких операторов. Каждый оператор отвечает за определённую логику выполнения, например агрегацию или соединение. |
Структура результата
Результат выполнения оператора EXPLAIN в Doris представляет собой полный PLAN. Внутри PLAN фрагменты FRAGMENTS расположены в порядке, обратном последовательности выполнения. Внутри каждого FRAGMENT операторы (PLAN NODES) также расположены в порядке, обратном последовательности выполнения.
Ниже приведён пример:
+
| Explain String(Nereids Planner) |
+
| PLAN FRAGMENT 0 |
| OUTPUT EXPRS: |
| cnt[
| cnt[
| PARTITION: UNPARTITIONED |
| |
| HAS_COLO_PLAN_NODE: false |
| |
| VRESULT SINK |
| MYSQL_PROTOCAL |
| |
| 7:VEXCHANGE |
| offset: 0 |
| distribute expr lists: |
| |
| PLAN FRAGMENT 1 |
| |
| PARTITION: RANDOM |
| |
| HAS_COLO_PLAN_NODE: false |
| |
| STREAM DATA SINK |
| EXCHANGE ID: 07 |
| UNPARTITIONED |
| |
| 6:VHASH JOIN(354) |
| | join op: INNER JOIN(BROADCAST)[] |
| | equal join conjunct: cnt[
| | cardinality=1 |
| | vec output tuple id: 8 |
| | vIntermediate tuple ids: 7 |
| | hash output slot ids: 5 7 |
| | distribute expr lists: |
| | distribute expr lists: |
| | |
| |
| | offset: 0 |
| | distribute expr lists: |
| | |
| 5:VEXCHANGE |
| offset: 0 |
| distribute expr lists: |
| |
| PLAN FRAGMENT 2 |
| ... |
| |
| PLAN FRAGMENT 3 |
| ... |
+
Операторы связаны со своими дочерними узлами пунктирными линиями. Если у оператора несколько дочерних узлов, они располагаются по вертикали в порядке справа налево. В приведённом выше примере у оператора 6 (VHASH JOIN) левый дочерний узел — оператор 5 (EXCHANGE), а правый — оператор 4 (EXCHANGE).
Описание полей фрагмента
| Название | Описание |
|---|
| PARTITION | Показывает распределение данных текущего фрагмента |
| HAS_COLO_PLAN_NODE | Указывает, содержит ли фрагмент операторы colocate |
| Sink | Способ вывода данных фрагмента; подробности приведены в таблице ниже |
Способы вывода данных через Sink
| Название | Описание |
|---|
| STREAM DATA SINK | Выводит данные в следующий фрагмент. Содержит две строки информации. Первая строка: последующий EXCHANGE NODE, которому отправляются данные. Вторая строка: способ распределения данных. - UNPARTITIONED означает, что каждый последующий экземпляр получает полный набор данных. Обычно это используется при широковещательных соединениях или когда логика должна выполняться в одном экземпляре, например для глобального limit или order by. - RANDOM означает, что каждый последующий экземпляр получает случайное подмножество данных без повторений. - HASH_PARTITIONED использует перечисленные слоты в качестве ключей хеширования, чтобы направлять части данных одному и тому же последующему экземпляру. Этот способ часто используется перед партиционированными хеш-соединениями или вторым этапом двухэтапной агрегации. |
| RESULT SINK | Отправляет результирующие данные в FE. Первая строка указывает протокол передачи данных; в настоящее время поддерживаются протоколы MySQL и arrow. |
| OLAP TABLE SINK | Записывает данные в таблицу OLAP. |
| MultiCastDataSinks | Оператор многоадресной передачи, содержащий несколько STREAM DATA SINK. Каждый STREAM DATA SINK отправляет полный набор данных своему последующему узлу. |
В режиме VERBOSE выводится информация о кортежах. Она описывает характеристики SLOT в строке данных, включая тип SLOT, допустимость пустых значений и другие свойства.
Вывод содержит несколько TupleDescriptors, каждый из которых содержит несколько SlotDescriptors. Ниже приведён пример:
Tuples:
TupleDescriptor{id=0, tbl=t1}
SlotDescriptor{id=0, col=c1, colUniqueId=0, type=int, nullable=true, isAutoIncrement=false, subColPath=null}
SlotDescriptor{id=2, col=c3, colUniqueId=2, type=int, nullable=true, isAutoIncrement=false, subColPath=null}
TupleDescriptor
| Название | Описание |
|---|
| id | Идентификатор дескриптора кортежа |
| tbl | Таблица, соответствующая кортежу, или null, если неприменимо |
SlotDescriptor
| Название | Описание |
|---|
| id | Идентификатор дескриптора слота |
| col | Столбец, соответствующий слоту; если неприменимо, поле остаётся пустым |
| colUniqueId | Уникальный идентификатор соответствующего столбца или -1, если неприменимо |
| type | Тип слота |
| nullable | Указывает, могут ли соответствующие данные принимать значение null |
| isAutoIncrement | Указывает, является ли столбец автоинкрементным |
| subColPath | Путь к вложенному столбцу внутри столбца; в настоящее время применим только к типам variant |
Описание операторов
Список операторов
| Название | Описание |
|---|
| AGGREGATE | Оператор агрегации |
| ANALYTIC | Оператор оконной функции |
| ASSERT NUMBER OF ROWS | Оператор проверки количества строк, выводимых последующими узлами |
| EXCHANGE | Оператор приёма данных при обмене |
| MERGING-EXCHANGE | Оператор приёма данных при обмене с поддержкой сортировки и ограничения количества строк |
| HASH JOIN | Оператор хеш-соединения |
| NESTED LOOP JOIN | Оператор соединения вложенными циклами |
| PartitionTopN | Оператор предварительной фильтрации данных внутри партиции |
| REPEAT_NODE | Оператор дублирования данных |
| DataGenScanNode | Оператор функции, возвращающей таблицу |
| EsScanNode | Оператор сканирования таблицы ES |
| HIVE_SCAN_NODE | Оператор сканирования таблицы Hive |
| HUDI_SCAN_NODE | Оператор сканирования таблицы Hudi |
| ICEBERG_SCAN_NODE | Оператор сканирования таблицы Iceberg |
| PAIMON_SCAN_NODE | Оператор сканирования таблицы Paimon |
| JdbcScanNode | Оператор сканирования таблицы Jdbc |
| OlapScanNode | Оператор сканирования таблицы Olap |
| SELECT | Оператор фильтрации |
| UNION | Оператор объединения множеств |
| EXCEPT | Оператор разности множеств |
| INTERSECT | Оператор пересечения множеств |
| SORT | Оператор сортировки |
| TOP-N | Оператор сортировки и возврата первых N результатов |
| TABLE FUNCTION NODE | Оператор табличной функции (lateral view) |
Общие поля
| Название | Описание |
|---|
| limit | Ограничивает количество выводимых строк |
| offset | Количество строк, пропускаемых перед выводом |
| conjuncts | Фильтрует результаты текущего узла. Выполняется до проекций. |
| projections | Операции проекции после текущего оператора. Выполняются после conjuncts. |
| project output tuple id | Выходной кортеж после проекции. Расположение слотов в кортеже данных можно посмотреть в tuple desc. |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| distribute expr lists | Исходный способ распределения данных дочерних узлов текущего узла |
| ID слота выражения | Конкретный слот, соответствующий ID, можно найти в списке кортежей в режиме verbose. Этот список содержит такие сведения, как тип слота и допустимость пустых значений. Обозначается как [#5] после выражения. |
AGGREGATE
| Название | Описание |
|---|
| (Фаза агрегации) | Фаза агрегации обозначается двумя терминами. Первый термин может быть update (локальная агрегация) или merge (глобальная агрегация). Второй термин указывает, сериализованы ли текущие данные (serialize) или для них уже завершены итоговые вычисления (finalize). |
| STREAMING | Этот флаг есть только у локальных операторов агрегации при усечении многоэтапной агрегации. Он указывает, что текущий узел агрегации может использовать режим STREAMING, в котором входные данные передаются непосредственно на следующий этап агрегации без фактических вычислений. |
| output | Вывод текущего оператора агрегации. Все локальные функции предварительной агрегации имеют префикс partial. |
| group by | Ключ агрегации |
ANALYTIC
| Название | Описание |
|---|
| functions | Имя текущей оконной функции |
| partition by | Соответствует предложению partition by внутри предложения over оконной функции. Выражение разбиения на окна. |
| order by | Выражение и порядок сортировки внутри окна |
| window | Границы окна |
ASSERT NUMBER OF ROWS
| Название | Описание |
|---|
| EQ | Количество строк на выходе последующих узлов должно соответствовать этому ограничению |
HASH JOIN
| Название | Описание |
|---|
| join op | Тип соединения |
| equal join conjunct | Условие равенства в условии соединения |
| other join predicates | Условия соединения, кроме равенства |
| mark join predicates | Условия, используемые в mark join |
| other predicates | Предикаты фильтрации после выполнения соединения |
| runtime filters | Созданные фильтры времени выполнения |
| output slot ids | Список слотов итогового вывода |
| hash output slot ids | Список выходных слотов после выполнения хеш-соединения, но до применения остальных условий соединения |
| isMarkJoin | Указывает, является ли соединение mark join |
NESTED LOOP JOIN
| Название | Описание |
|---|
| join op | Тип операции соединения |
| join conjuncts | Условия соединения |
| mark join predicates | Условия, используемые в mark join |
| predicates | Предикаты фильтрации после соединения |
| runtime filters | Созданные фильтры времени выполнения |
| output slot ids | Список слотов итогового вывода |
| isMarkJoin | Является ли соединение mark join |
PartitionTopN
| Название | Описание |
|---|
| functions | Оконные функции, для которых применяется оптимизация фильтрации по группам |
| has global limit | Наличие глобального ограничения количества строк |
| partition limit | Ограничение количества строк внутри каждой партиции |
| partition topn phase | Текущая фаза: TWO_PHASE_GLOBAL_PTOPN для глобальной фазы после перераспределения по ключу партиционирования, TWO_PHASE_LOCAL_PTOPN для локальной фазы до перераспределения по ключу партиционирования |
REPEAT_NODE
| Название | Описание |
|---|
| repeat | Количество повторений каждой строки и соответствующие ID слотов столбцов агрегации |
| exprs | Список выражений для выходных данных после повторения |
DataGenScanNode
| Название | Описание |
|---|
| table value function | Имя табличной функции |
EsScanNode
| Название | Описание |
|---|
| SORT COLUMN | Столбцы для сортировки результатов |
| LOCAL_PREDICATES | Фильтры, выполняемые в Doris |
| REMOTE_PREDICATES | Фильтры, выполняемые в ES |
| ES index/type | Индекс и тип ES для запроса |
HIVE_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
HUDI_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
| hudiNativeReadSplits | Количество частей, читаемых нативным способом |
ICEBERG_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
| icebergPredicatePushdown | Фильтры, переданные в API iceberg |
PAIMON_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
| paimonNativeReadSplits | Количество частей, читаемых нативным способом |
NESTED LOOP JOIN
| Название | Описание |
|---|
| join op | Тип операции соединения |
| join conjuncts | Условия соединения |
| mark join predicates | Условия, используемые в mark join |
| predicates | Предикаты фильтрации после соединения |
| runtime filters | Созданные фильтры времени выполнения |
| output slot ids | Список слотов итогового вывода |
| isMarkJoin | Является ли соединение mark join |
PartitionTopN
| Название | Описание |
|---|
| functions | Оконные функции, для которых применяется оптимизация фильтрации по группам |
| has global limit | Наличие глобального ограничения количества строк |
| partition limit | Ограничение количества строк внутри каждой партиции |
| partition topn phase | Текущая фаза: TWO_PHASE_GLOBAL_PTOPN для глобальной фазы после перераспределения по ключу партиционирования, TWO_PHASE_LOCAL_PTOPN для локальной фазы до перераспределения по ключу партиционирования |
REPEAT_NODE
| Название | Описание |
|---|
| repeat | Количество повторений каждой строки и соответствующие ID слотов столбцов агрегации |
| exprs | Список выражений для выходных данных после повторения |
DataGenScanNode
| Название | Описание |
|---|
| table value function | Имя табличной функции |
EsScanNode
| Название | Описание |
|---|
| SORT COLUMN | Столбцы для сортировки результатов |
| LOCAL_PREDICATES | Фильтры, выполняемые в Doris |
| REMOTE_PREDICATES | Фильтры, выполняемые в ES |
| ES index/type | Индекс и тип ES для запроса |
HIVE_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
HUDI_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
| hudiNativeReadSplits | Количество частей, читаемых нативным способом |
ICEBERG_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
| icebergPredicatePushdown | Фильтры, переданные в API iceberg |
PAIMON_SCAN_NODE
| Название | Описание |
|---|
| inputSplitNum | Количество частей сканирования |
| totalFileSize | Общий размер сканируемых файлов |
| scanRanges | Информация о частях сканирования |
| partition | Количество сканируемых партиций |
| backends | Сведения о данных, назначенных для сканирования каждому BE |
| cardinality | Оценочное количество строк, рассчитанное оптимизатором |
| avgRowSize | Оценочный средний размер строки, рассчитанный оптимизатором |
| numNodes | Количество BE, используемых текущим оператором |
| pushdown agg | Агрегации, переданные на уровень сканирования |
| paimonNativeReadSplits | Количество частей, читаемых нативным способом |
JdbcScanNode
| Название | Описание |
|---|
| TABLE | Имя сканируемой таблицы на стороне JDBC |
| QUERY | Запрос, используемый для сканирования |
OlapScanNode
| Название | Описание |
|---|
| TABLE | Сканируемая таблица. В скобках указано имя выбранного синхронного материализованного представления. |
| SORT INFO | Присутствует, когда запланирована предварительная сортировка SCAN. Указывает на частичную предварительную сортировку и предварительное усечение вывода SCAN. |
| SORT LIMIT | Присутствует, когда запланирована предварительная сортировка SCAN. Указывает длину, до которой выполняется предварительное усечение. |
| TOPN OPT | Присутствует, когда запланировано применение TOP-N Runtime Filter. |
| PREAGGREGATION | Указывает, включена ли предварительная агрегация. Применимо к моделям с агрегацией MOR и первичным ключом. ON означает, что данные на уровне хранения удовлетворяют требованиям вышележащего уровня без дополнительной агрегации. OFF означает, что выполняется дополнительная агрегация. |
| partitions | Количество сканируемых в данный момент партиций, общее количество партиций и список имён сканируемых партиций. |
| tablets | Количество сканируемых таблетов и общее количество таблетов в таблице. |
| tabletList | Список сканируемых таблетов. |
| avgRowSize | Оценочный размер строки, рассчитанный оптимизатором. |
| numNodes | Количество BE, назначенных для текущего сканирования. |
| pushAggOp | Результаты возвращаются путём чтения метаданных zonemap. Поддерживается информация об агрегациях MIN, MAX, COUNT. |
UNION
| Название | Описание |
|---|
| constant exprs | Список константных выражений, включаемых в вывод. |
| child exprs | Выходные данные дочерних узлов проецируются с помощью этого списка выражений и подаются на вход оператору над множествами. |
EXCEPT
| Название | Описание |
|---|
| child exprs | Выходные данные дочерних узлов проецируются с помощью этого списка выражений и подаются на вход оператору над множествами. |
INTERSECT
| Название | Описание |
|---|
| child exprs | Выходные данные дочерних узлов проецируются с помощью этого списка выражений и подаются на вход оператору над множествами. |
SORT
| Название | Описание |
|---|
| order by | Ключ сортировки и конкретный порядок сортировки. |
TABLE FUNCTION NODE
| Название | Описание |
|---|
| table function | Имя используемой табличной функции. |
| lateral view tuple id | ID кортежа, соответствующего вновь созданным столбцам. |
| output slot id | Список ID слотов для столбцов, выводимых после отсечения столбцов. |
TOP-N
| Название | Описание |
|---|
| order by | Ключ сортировки и конкретный порядок сортировки. |
| TOPN OPT | Присутствует при применении оптимизации фильтра времени выполнения TOP-N. |
| OPT TWO PHASE | Присутствует при применении отложенной материализации TOP-N. |