Типы EXPLAIN
AST— Абстрактное синтаксическое дерево.SYNTAX— Текст запроса после оптимизаций на уровне AST.QUERY TREE— Дерево запроса после оптимизаций на уровне дерева запроса.PLAN— План выполнения запроса.PIPELINE— Конвейер выполнения запроса.ANALYZE— Выполняет запрос и дополняет план выполнения измеренными метриками времени выполнения.ESTIMATE— Оценочное количество строк, меток и частей, которые будут прочитаны из таблиц при обработке запроса.TABLE OVERRIDE— Провалидированный результат переопределения таблицы в схеме табличной функции.
EXPLAIN AST
SELECT.
Настройки:
graph– Выводит AST в виде графа, описанного на языке описания графов DOT. По умолчанию: 0.
EXPLAIN SYNTAX
oneline– Выводить запрос в одну строку. По умолчанию:0.run_query_tree_passes– Выполнять проходы по дереву запроса перед выводом дерева запроса. По умолчанию:0.query_tree_passes– Если заданоrun_query_tree_passes, указывает, сколько проходов выполнить. Еслиquery_tree_passesне указано, выполняются все проходы.
Query
Response
run_query_tree_passes:
Query
Response
EXPLAIN QUERY TREE
run_passes— Выполнить все проходы по дереву запроса перед его выводом. По умолчанию:1.dump_passes— Вывести информацию об использованных проходах по дереву запроса перед выводом дерева запроса. По умолчанию:0.passes— Указывает, сколько проходов по дереву запроса выполнить. Если задано значение-1, выполняются все проходы по дереву запроса. По умолчанию:-1.dump_tree— Показать дерево запроса. По умолчанию:1.dump_ast— Показать AST запроса, сгенерированное из дерева запроса. По умолчанию:0.
EXPLAIN PLAN
optimize— Управляет тем, применять ли оптимизации плана запроса перед его отображением. Значение по умолчанию: 1.header— Выводит заголовок для шага. Значение по умолчанию: 0.description— Выводит описание шага. Значение по умолчанию: 1.indexes— Показывает используемые индексы, количество отфильтрованных частей и количество отфильтрованных гранул для каждого применённого индекса. Значение по умолчанию: 0. Поддерживается для таблиц MergeTree. Начиная с ClickHouse >= v25.9, этот оператор показывает осмысленный результат только при использовании сSETTINGS use_query_condition_cache = 0, use_skip_indexes_on_data_read = 0.projections— Показывает все проанализированные проекции и их влияние на фильтрацию на уровне частей на основе условий по первичному ключу проекции. Для каждой проекции в этом разделе приводится статистика, включая количество частей, строк, меток и диапазонов, оценённых с использованием первичного ключа проекции. Также показывается, сколько частей данных было пропущено благодаря этой фильтрации без чтения из самой проекции. Была ли проекция действительно использована для чтения или только проанализирована для фильтрации, можно определить по полюdescription. Значение по умолчанию: 0. Поддерживается для таблиц MergeTree.actions— Выводит подробную информацию о действиях шага. Значение по умолчанию: 1.sorting— Выводит описание сортировки для каждого шага плана, который формирует отсортированный вывод. Значение по умолчанию: 0.keep_logical_steps— Сохраняет логические шаги плана для JOIN вместо преобразования их в физические реализации JOIN. Значение по умолчанию: 0.json— Выводит шаги плана запроса как строку в формате JSON. Значение по умолчанию: 0. Чтобы избежать лишнего экранирования, рекомендуется использовать формат TabSeparatedRaw (TSVRaw).input_headers— Выводит входные заголовки для шага. Значение по умолчанию: 0. В основном полезно только разработчикам для отладки проблем, связанных с несоответствием входных и выходных заголовков.column_structure— Также выводит структуру столбцов в заголовках помимо их имени и типа. Значение по умолчанию: 0. В основном полезно только разработчикам для отладки проблем, связанных с несоответствием входных и выходных заголовков.distributed— Показывает планы запроса, выполняемые на удалённых узлах для distributed таблиц или параллельных реплик. Не поддерживается вместе сjson. Значение по умолчанию: 0.compact— Если включено, скрывает из плана шаги выражений и подробную информацию о действиях (входы, функции, псевдонимы и позиции вывода). Действует только приactions = 1. Значение по умолчанию: 1.pretty— Выводит дерево плана с использованием символов построения линий (├──, └──, │) вместо отступов для наглядного отображения иерархии. Также форматирует свойства шага JOIN в одну строку. Значение по умолчанию: 1.
По умолчанию
explain_query_plan_default = 'pretty', поэтому actions, compact и pretty инициализируются значением 1, а план отображается в компактном, наглядном виде с аннотациями действий. Явное указание любого из этих параметров в операторе EXPLAIN (например, EXPLAIN actions = 0, compact = 0, pretty = 0 SELECT ...) всегда переопределяет значение по умолчанию.До ClickHouse 26.7 значениями по умолчанию для actions, compact и pretty были 0. Этот вывод по-прежнему можно получить, установив explain_query_plan_default = 'legacy' (глобально или в SETTINGS для отдельного запроса) либо задав compatibility любой версии старше 26.7.Параметры json и distributed не включают значения по умолчанию для pretty (actions, compact и pretty), даже когда explain_query_plan_default = 'pretty'. Чтобы включить подробности о действиях в их вывод, вручную задайте actions = 1.Оценка стоимости шагов и запроса не поддерживается.
json = 1 план запроса представляется в формате JSON. Каждый узел — это словарь, который всегда содержит ключи Node Type, Node Id и Plans. Node Type — строка с именем шага, а Node Id — уникальный идентификатор шага (имя шага с числовым суффиксом, например Union_10). Plans — массив с описаниями дочерних шагов. В зависимости от типа узла и настроек могут добавляться и другие необязательные ключи.
Пример:
description = 1 в шаг добавляется ключ Description:
header = 1 в шаг добавляется ключ Header в виде массива столбцов.
Пример:
indexes = 1 добавляется ключ Indexes. Он содержит массив использованных индексов. Каждый индекс описывается в формате JSON с ключом Type (строка Partition Min-Max, Partition, Statistics, PrimaryKey или Skip) и следующими необязательными ключами:
Name— имя индекса (в настоящее время используется только для индексовSkip).Keys— массив столбцов, используемых индексом.Condition— используемое условие.Description— описание индекса (в настоящее время используется только для индексовSkip).Parts— количество частей после/до применения индекса.Granules— количество гранул после/до применения индекса.Ranges— количество диапазонов гранул после применения индекса.
projections = 1 добавляется ключ Projections. Он содержит массив проанализированных проекций. Каждая проекция описывается в формате JSON со следующими ключами:
Name— Имя проекции.Condition— Используемое условие по первичному ключу проекции.Description— Описание того, как используется проекция (например, для фильтрации на уровне частей).Selected Parts— Количество частей, выбранных проекцией.Selected Marks— Количество выбранных меток.Selected Ranges— Количество выбранных диапазонов.Selected Rows— Количество выбранных строк.Filtered Parts— Количество частей, пропущенных из-за фильтрации на уровне частей.
actions = 1 добавляемые ключи зависят от типа шага.
Пример:
compact = 0 и actions = 1 отображаются шаги Expression вместе с подробной информацией о выражениях:
distributed = 1 вывод включает не только локальный план запроса, но и планы запросов, которые будут выполняться на удалённых узлах. Это полезно для анализа и отладки распределённых запросов.
distributed отображается только в формате legacy (без pretty), поскольку вывод pretty не встраивает планы удалённых сегментов в дерево плана. По этой причине включение distributed автоматически отключает связанные с pretty значения по умолчанию (actions, compact и pretty) независимо от explain_query_plan_default. Вы по-прежнему можете задать actions=1 вручную. Параметр distributed также не поддерживается вместе с json.pretty = 1 дерево плана отображается с использованием символов псевдографики вместо отступов, а для ключевых шагов показывается дополнительная информация:
- Выходные столбцы запроса выводятся в верхней части плана.
- Выражения в фильтрах, ключах агрегации, описаниях сортировки и оконных функциях отображаются в человекочитаемой SQL-подобной нотации (например,
a + 1 > 5вместоgreater(plus(a, 1), 5)). Для наглядности внутренние префиксы идентификаторов столбцов (например,__table1.) удаляются. - Исходные шаги (например,
ReadFromMergeTree) отображают свои выходные столбцы. - Шаги фильтрации отображают условие фильтрации в SQL-нотации. Если присутствуют runtime-фильтры JOIN, они показываются отдельно.
- Шаги агрегации отображают ключи и агрегатные функции с их аргументами (например,
sum(c),count()). - Множества
IN, заданные кортежными литералами, показывают свои значения (усечённые для больших множеств), множества на основе подзапросов помечаются какsubquery1,subquery2и т. д., а множества из таблиц с движкомSetпоказывают имя таблицы. - Шаги JOIN отображают отношение JOIN в математической нотации, оценочное количество строк в результате, а также то, какие выходные столбцы поступают с левой, а какие — с правой стороны. Для представления различных типов JOIN используются следующие символы:
Например,
t1 ⟕ t2 означает Left JOIN между таблицами t1 и t2.
Число в скобках после имени таблицы (например, t1[100]) указывает на оценочное количество строк,
если доступна статистика таблицы.
Параметр pretty хорошо работает вместе с compact = 1, который скрывает шаги Expression и подробную информацию о действиях, делая план более удобным для чтения.
Подробный пример с JOIN:
EXPLAIN PIPELINE
header— Выводит заголовок для каждого выходного порта. По умолчанию: 0.graph— Выводит граф, описанный на языке описания графов DOT. По умолчанию: 0.compact— Выводит граф в компактном режиме, если включена настройкаgraph. По умолчанию: 1.compact_repeated_processor_chains— Объединяет соседние повторяющиеся цепочки процессоров в текстовом выводе, показывая одну копию цепочки с числом повторений. Это может упростить чтение параллельных конвейеров, когда одна и та же цепочка встречается много раз, например при JOIN. На вывод графа это не влияет. По умолчанию: 0.
compact=0 и graph=1, имена процессоров будут содержать дополнительный суффикс с уникальным идентификатором процессора.
Пример:
EXPLAIN ANALYZE
EXPLAIN ANALYZE действительно выполняет запрос, отбрасывает строки результата и выводит то же дерево плана, что и EXPLAIN PLAN, добавляя к каждому шагу сведения о том, что реально произошло во время выполнения.
Настройки:
EXPLAIN ANALYZE поддерживает те же параметры отображения, что и EXPLAIN PLAN (они описаны в разделе EXPLAIN PLAN).
header— см. раздел EXPLAIN PLAN.description— см. раздел EXPLAIN PLAN.projections— см. раздел EXPLAIN PLAN.sorting— см. раздел EXPLAIN PLAN.input_headers— см. раздел EXPLAIN PLAN.column_structure— см. раздел EXPLAIN PLAN.actions— см. раздел EXPLAIN PLAN. По умолчанию: 1.indexes— см. раздел EXPLAIN PLAN. По умолчанию: 1.compact— см. раздел EXPLAIN PLAN. По умолчанию: 1.pretty— см. раздел EXPLAIN PLAN. По умолчанию: 1.processors— дляEXPLAIN ANALYZEвыводит дополнительную строку для каждого этапа с распределением времени выполнения по каждому процессору:min,median,maxиsum. Это полезно для выявления перекоса нагрузки между параллельными процессорами. По умолчанию: 0.matches— дляEXPLAIN ANALYZEзаставляет шаги JOIN выполнять дополнительный учёт, необходимый для метрикmatched,match rateиfanoutв случаях, когда эти значения невозможно вывести из результатов JOIN. Когда это возможно, они выводятся и без этой опции. См. Шаги JOIN. По умолчанию: 0.
Поскольку
EXPLAIN ANALYZE действительно выполняет обёрнутый запрос, он ведёт себя как
этот запрос — и, в отличие от форм EXPLAIN, которые не выполняют запрос, — в нескольких отношениях:- Квоты и ограничения. Он учитывается в тех же квоты
и подпадает под те же ограничения
(например,
query_selects,read_rows), что и при прямом выполнении запроса. Источники, освобождённые от квот на этапе планирования (такие какsystem.one), не учитываются. - Неуспешные транзакции. Внутри transaction,
которая уже завершилась ошибкой (
ROLLED_BACK), он отклоняется сINVALID_TRANSACTION, так же как и обычныйSELECT— сначала выполнитеROLLBACK. - Потоковые чтения. При потоковом чтении (
FROM ... STREAM) он отклоняется сNOT_IMPLEMENTED, потому что такое чтение никогда не завершается. - Распределённые запросы. Он не поддерживается для запросов, выполняемых в режиме distributed.
Time— общее время, разделённое на этапы планирования (то есть создание плана + оптимизация плана + построение конвейера) и выполнения (запуск конвейера).Read— строки и несжатые байты, прочитанные из таблиц, с указанием пропускной способности — те же числа, которые нижний колонтитул обычного запроса показывает как “Processed”.Peak memory— пиковое потребление памяти запросом.
I/O). Время и параллелизм указываются для каждой стадии шага в следующих строках с отступом.
rows <in> → <out>— строки, вошедшие в шаг и вышедшие из него; (<selectivity>%) показывает, насколько шаг отфильтровал (out/in) или расширил данные; не показывается, если число входных строк равно числу выходных строк или если число входных строк равно0.<bytes_in> → <bytes_out>— несжатые байты в памяти, проходящие через шаг (не указывается, если оба значения равны нулю).time <t> (<share>%)— фактическое время, в течение которого стадия была активна, и её доля от времени выполнения запроса (то есть без времени сборки). Обратите внимание: сумма долей может превышать 100%, потому что стадии и шаги выполняются параллельно.parallelism <avg>/<max>— среднее число потоков CPU, одновременно работающих в пределах этой стадии, из максимально возможного числа. Значение, близкое к максимуму, означает, что стадия хорошо распараллелена; близкое к 1 — что она выполнялась в основном последовательно.Stage (<stage>)— имя стадии. Для шага с одной стадией строка времени выводится сразу, без меткиStage (...). Для шагов с несколькими стадиями выводится по одной помеченной строке на каждую стадию; например, дляAggregatingпоказываютсяStage (partial aggregation)иStage (final aggregation), а для hash JOIN —Stage (build)иStage (probe).
ClickHouse распараллеливает не только выполнение задач внутри шага плана, но и выполнение самих шагов плана. Метрика
parallelism отражает только работу этого шага. Другие шаги могут выполняться параллельно, поэтому это число не показывает, как параллелизм шага соотносится со всем запросом.Максимальное число в
parallelism вычисляется как минимум из:- общего числа задач внутри шага плана;
- максимального числа потоков обработки запроса, заданного в
max_threads.
Шаги JOIN
EXPLAIN ANALYZE выводит строки участия для каждой стороны — Left и Right, — за которыми следуют строки, относящиеся к конкретной реализации JOIN. Left и Right соответствуют логическим сторонам SQL. В большинстве случаев Left также является стороной проверки JOIN, а Right — стороной построения JOIN. Однако из-за перестановки сторон во время выполнения JOIN это не всегда так. Охвачены все значения join_algorithm (hash, parallel_hash, grace_hash, partial_merge, full_sorting_merge, parallel_full_sorting_merge, direct), а также две реализации, которые нельзя выбрать этой настройкой: JOIN типа CROSS или COMMA, JOIN с секцией ON без равенства ключей и движок таблицы Join. Большинство из них выводят сведения об обеих сторонах; некоторые — только о стороне, которую материализуют (например, direct выводит только Left:).
Строки для каждой стороны имеют одинаковую структуру:
EXPLAIN ANALYZE выводит:
rows <rows>— общее число строк этой стороны, прошедших через JOIN.matched <matched_rows>— число строк этой стороны, для которых нашлась хотя бы одна соответствующая строка на другой стороне JOIN. Подсчитываются строки, а не ключи: если ключ встречается три раза справа и имеет совпадение, все три строки справа считаются сопоставленными.match rate <match_rate>%— процент строк этой стороны, имеющих совпадение, вычисляемый как100 * <matched_rows> / <rows>.fanout <fanout>— среднее число выходных строк, создаваемых сопоставленной строкой этой стороны.
not collected, а не как 0. Значения match rate и fanout вычисляются на основе matched, поэтому для стороны, у которой оно отсутствует, все три значения указываются как not collected.
Разветвление
fanout показывает кратность строк:
NULL, для каждой строки сохраняемой стороны, не нашедшей пары. Эти строки вычитаются, чтобы не искажать соотношение. Они есть только у сохраняемой стороны: у правой для RIGHT и FULL, у левой для LEFT и FULL:
fanout = 0— сопоставленные строки вообще не дали строк результата, как и приANTIJOIN: он выводит только строки, не нашедшие пары.fanout = 1— корректное соединение 1:1; каждая сопоставленная строка дала ровно одну строку результата.fanout > 1— соединение 1:N; дублирующиеся ключи на другой стороне размножили строки. Большое значение сразу с обеих сторон — признак непреднамеренного разрастания декартова произведения.
Когда для подсчёта требуется matches = 1
EXPLAIN ANALYZE. Для остальных требуется дополнительный учёт, который JOIN иначе не выполнял бы, поэтому они выводятся только при использовании EXPLAIN ANALYZE matches = 1. Какие именно — зависит от алгоритма; в семействе хеш-алгоритмов это два случая:
- правая сторона
ALL INNERиALL LEFT, поскольку необходимо пометить каждую совпавшую правую строку; - левая сторона
ALL LEFTиALL FULL, но только если запрос не выбирает ничего из правой таблицы, а условиеONпредставляет собой простое равенство ключей. В противном случае проверка уже фиксирует, какие левые строки совпали — либо для материализации правых столбцов, либо для вычисления остаточного условия, — и количество совпадений будет точным без этой опции.
partial_merge требует её для правой стороны четырёх видов ALL по той же причине.
full_sorting_merge и parallel_full_sorting_merge требуют её для обеих сторон видов ANY. Для
видов ALL ничего не требуется.
По умолчанию опция отключена, поскольку дополнительный учёт требует ресурсов и снижает точность измерений. Работа выполняется внутри цикла проверки, а её объём растёт с числом строк результата. Используйте
matches = 1, когда необходимо узнать точное число совпадений, найденных левыми и правыми строками на другой стороне.matches = 1 не позволяет собирать данные для любой комбинации. Возможность вывода данных по той или иной стороне JOIN определяется тем,
что этот JOIN в любом случае должен делать, поэтому зависит как от алгоритма, так и от вида и
строгости.
Семейство хеш-алгоритмов. hash, parallel_hash и grace_hash всегда дают одинаковый результат:
Правая сторона недоступна, если JOIN хранит в своей хеш-таблице только одну строку на ключ, как это делают JOIN
ANY, SEMI и ANTI: дублирующиеся правые строки никогда не сохраняются, поэтому их невозможно подсчитать. Левая сторона недоступна, когда JOIN не выводит левую строку, соответствующая ей правая строка уже была сопоставлена с другой левой строкой, из-за чего число выведенных строк оказывается меньше числа совпавших.
Включение any_join_distinct_right_table_keys переключает ANY на прежнюю семантику RightAny, при которой выводится одна строка для каждой левой строки и поэтому сохраняются оба счётчика. В этом случае ANY RIGHT и ANY FULL выводят обе стороны, а ANY INNER переписывается в SEMI LEFT.
Движок таблицы Join следует той же таблице, используя вид и строгость, заданные в движке: Join(ALL, INNER, …) выводит обе стороны, Join(ANY, LEFT, …) — ни одной.
Алгоритмы слияния. full_sorting_merge и parallel_full_sorting_merge поддерживают четыре вида ALL, ANY INNER, ANY LEFT, ANY RIGHT, ASOF и ASOF LEFT. Они выводят обе стороны для всех видов, кроме ASOF и ASOF LEFT, где для правой стороны указано not collected, причём без matches = 1: они обходят два отсортированных входа и видят каждую строку диапазона равных значений по мере обработки, поэтому впоследствии ничего восстанавливать не требуется.
partial_merge поддерживает ALL INNER, ALL LEFT, ALL RIGHT, ALL FULL, ANY INNER, ANY LEFT и SEMI LEFT. Он выводит обе стороны для четырёх видов ALL, причём правую — с matches = 1; для ANY INNER, ANY LEFT и SEMI LEFT для правой стороны указано not collected.
direct. Только левая сторона. Правая сторона — это хранилище ключ-значение, которое никогда не материализуется в строки, поэтому строки Right: вообще нет.
CROSS, COMMA и константное условие ON. Ни одна из сторон, как описано выше.
Если оба алгоритма выдают число, эти числа совпадают. Алгоритмы слияния просто располагают более полной информацией; в вопросе о том, что считать совпадением, разногласий нет.
Строки для конкретных алгоритмов
hash и parallel_hash, а также для движка таблицы Join строка Hash table: описывает хеш-таблицу, построенную на основе правой таблицы:
unique keys <unique_keys>— количество уникальных ключей, хранящихся в хеш-таблице на этапе построения.memory <peak_memory>— пиковое потребление памяти хеш-таблицей на этапе построения.
grace_hash строка Hash table: также показывает, как JOIN адаптировался к ограничению памяти, а строка Spill: — были ли данные сброшены на диск:
buckets <buckets>— число бакетов, которое алгоритм grace hash join использовал к завершению выполнения. Это всегда степень2.rehashes <rehashes>— сколько раз пришлось удвоить число бакетов, чтобы уложиться в лимит памяти.Spill:— флагyes/no, указывающий, выполнялась ли запись промежуточных данных на диск. Если да,left spilled <left_spilled_bytes>иright spilled <right_spilled_bytes>показывают объём сжатых данных, записанных на диск с левой (проверки) и правой (build) сторон; если ничего не записывалось на диск, строка имеет видSpill: no.
partial_merge строка Right: содержит дополнительную информацию о буферизации и сортировке правой таблицы, а время сортировки указано в строках Stage (build) и Stage (проверки):
size <right_size>— объём памяти, занимаемый блоками правой таблицы.blocks <right_blocks>— количество блоков, в которые была буферизована правая таблица.storage <in-memory|external>— поместилась ли правая таблица в памяти (in-memory) или её пришлось записать на диск (external). При значенииexternalдополнительный параметрspilled <spilled_bytes>указывает количество сжатых байтов, записанных на диск.sort time <sort_time>— время, затраченное на сортировку правой таблицы (на стадии build) и каждого входящего левого блока (на стадии проверки).sort share <sort_share>%— доляsort timeв собственном времени занятости этой стадии (сумме времени выполнения её процессоров), в отличие от процентаtimeстадии, который представляет собой долю общего времени выполнения запроса.
full_sorting_merge выводятся только общие строки Left: и Right:.
Для JOIN direct выводится только строка Left:, поскольку правая сторона представляет собой хранилище ключ-значение, поиск в котором выполняется напрямую, а не материализуется в строки.
Для JOIN CROSS или COMMA, а также для любого раздела ON без равенства ключей строка Buffer: описывает, как правая таблица хранилась в памяти, а строка Spill: сообщает, была ли она записана на диск:
memory <peak_memory>— пиковое потребление памяти буферизованной правой таблицей.compressed <yes|no>— был ли сжат хотя бы один буферизованный блок; в этом случае reader распаковывает каждый сохранённый блок.Spill:— тот же флагyes/no, что и дляgrace_hash;right spilled <right_spilled_bytes>указывает объём сжатых байтов, записанных на диск.
matched not collected: постоянный предикат либо сопоставляет каждую левую строку с каждой правой строкой, либо не сопоставляет ни одной, поэтому определить, какие именно строки совпали, невозможно.
Для JOIN с движком таблицы Join отображаются обе стороны, а также строка Hash table:, описывающая предварительно созданную таблицу. Для правой стороны указывается число строк, хранящихся в движке, а не число строк, созданных при построении для отдельного запроса.
Время по процессорам
processors = 1 под каждой стадией выводится дополнительная строка, показывающая распределение затраченного времени между процессорами этой стадии:
<n> — это количество процессоров на стадии. Большой разрыв между median и max указывает на неравномерное распределение нагрузки между параллельными процессорами.
EXPLAIN ESTIMATE
Query
Query
Response
EXPLAIN WHATIF
SELECT, без материализации индекса на диске. Задайте один или несколько кандидатов с помощью CREATE HYPOTHETICAL INDEX, затем выполните EXPLAIN WHATIF SELECT ..., чтобы увидеть для каждого кандидата: применимость, оценочное количество прочитанных меток, оценочный объём данных в байтах и коэффициент пропуска.
Синтаксис
empirical—1(по умолчанию) запускает индекс в памяти на гранулах, отобранных после базовой фильтрации, чтобы измерить коэффициент пропуска (верхнюю границу).0пропускает этот этап. В любом случае, если empirical не даёт результата (отключён или индекс нельзя вычислить в памяти), оценщик переключается на статистику столбцов, а затем — на сводку только по применимости, если недоступно ни то, ни другое.
source— как была получена оценка.empirical: индекс строится в памяти по гранулам, оставшимся после базового pruning, и подсчитывается, сколько гранул индекс мог бы пропустить. Это верхняя граница — см. ограничения вCREATE HYPOTHETICAL INDEX.statistical: вычисляется на основе статистики столбцов. Используется, когда empirical отключён (empirical = 0) или empirical не смог дать результат, а для соответствующих столбцов задана статистика.applicability_only: индекс применим к предикату, но ни эмпирическая, ни статистическая оценка не дали результата (например,empirical = 0и статистика столбцов не задана). Возвращаетskip_ratio: 0.0%как консервативную границу.
sampled_parts/sampled_marks—<baseline-pruned> / <total in the table>. Показывает, какая доля таблицы осталась после pruning по PK, партициям и существующим индексам, то есть какие данные поступают на вход гипотетическому индексу.est_bytes— оценка количества прочитанных байтов, полученная на основе среднего размера строки в таблице, поэтому она приблизительна и зависит от хранилища и сжатия. Строка baseline появляется только тогда, когда запрос читает строки; строка для каждого кандидата — только когда известна базовая оценка объёма в байтах.
WHATIF и SELECT — ключевое слово SETTINGS отсутствует (это соответствует тому, как другие варианты EXPLAIN принимают свои параметры).
Если для таблицы не определены гипотетические индексы, EXPLAIN WHATIF возвращает status: not_applicable с подсказкой создать индекс.
Комбинированная строка (несколько кандидатов)
Когда два или более кандидата оцениваются эмпирически, EXPLAIN WHATIF добавляет ещё один блок с именем (combined: idx_a, idx_b, ...) после строк отдельных кандидатов. Он показывает совокупную пользу от наличия всех этих индексов одновременно: при реальном чтении гранула сохраняется только в том случае, если проходит через каждый индекс пропуска данных, поэтому комбинированная оценка представляет собой пересечение гранул, оставшихся после кандидатов. Следовательно, его skip_ratio как минимум не ниже, чем у лучшего отдельного кандидата: взаимодополняющие индексы вместе отсекают больше, а избыточные не меняют результат.
Учитываются только кандидаты с source: empirical, поскольку объединённая строка формируется путём пересечения их наборов выживания по гранулам. Кандидаты с оценкой statistical или applicability_only не имеют данных по гранулам и исключаются; соответственно, объединённый блок появляется только тогда, когда как минимум два кандидата дали эмпирическую оценку, и в остальных случаях опускается (например, при empirical = 0). Его поля оценки совпадают с полями эмпирического блока отдельного кандидата, за исключением elapsed_us, которое равно 0 — объединённая оценка выводится на основе сканирований отдельных кандидатов, а не нового сканирования. Синтетическое имя (combined: ...) служит только меткой в отчёте и не может использоваться с force_data_skipping_indices.
Эмпирический пример
minmax сократил бы число меток со 100 до 1 — skip_ratio: 99.0%. (est_bytes — это оценка, основанная на среднем размере строки, поэтому точное значение может отличаться.)
Статистический пример
Статистика столбцов по умолчанию отключена. Чтобы задействовать вариант statistical, сначала задайте её для нужных столбцов и дождитесь завершения мутации materialize:
b < 10 (примерно 10 строк из 10000) и приводится как верхняя граница для skip_ratio. Значения sampled_parts / sampled_marks отсутствуют — данные не считывались.
Если ни один из вариантов недоступен (например, empirical = 0 и статистика столбцов не определена), оценщик возвращает source: applicability_only и консервативное значение skip_ratio: 0.0%.
EXPLAIN TABLE OVERRIDE
Query
Query
Response
Проверка не является исчерпывающей, поэтому успешный запрос не гарантирует, что переопределение не приведёт к проблемам.