Spec-Zone.ru › MySQL 8.4

10.8.2 Формат вывода EXPLAIN

Оператор EXPLAIN предоставляет информацию о том, как MySQL выполняет операторы. EXPLAIN работает с операторами SELECT, DELETE, INSERT, REPLACE и UPDATE.

EXPLAIN возвращает строку информации для каждой таблицы, используемой в операторе SELECT. Он перечисляет таблицы в выводе в порядке, в котором MySQL их считывает при обработке оператора. Это означает, что MySQL считывает строку из первой таблицы, затем находит соответствующую строку во второй таблице, а затем в третьей и так далее. Когда все таблицы обработаны, MySQL выводит выбранные столбцы и возвращается к списку таблиц до тех пор, пока не найдется таблица, для которой имеется больше соответствующих строк. Следующая строка считывается из этой таблицы, и процесс продолжается с последующей таблицей.

Примечание

MySQL Workbench имеет возможность визуального объяснения, которая предоставляет визуальное представление вывода EXPLAIN. См. .

  • Столбцы вывода EXPLAIN

  • Типы соединений EXPLAIN

  • Дополнительная информация EXPLAIN

  • Интерпретация вывода EXPLAIN

Столбцы вывода EXPLAIN

В этом разделе описаны столбцы вывода, полученные оператором EXPLAIN. В последующих разделах содержится дополнительная информация о столбцах type и Extra.

Каждая строка вывода из EXPLAIN содержит информацию об одной таблице. Каждая строка содержит значения, суммированные в таблице 10.1 «Столбцы вывода EXPLAIN» и описанные более подробно после таблицы. Названия столбцов показаны в первом столбце таблицы; во втором столбце показано эквивалентное имя свойства, показанное в выводе, когда FORMAT=JSON используется.

Таблица 10.1 Столбцы вывода EXPLAIN

Таблица 10.1 Столбцы вывода EXPLAIN
Столбец Имя JSON Значение
id select_id Идентификатор SELECT
select_type None Тип SELECT
table table_name Таблица для строки вывода
partitions partitions Соответствующие разделы
type access_type Тип соединения
possible_keys possible_keys Возможные индексы для выбора
key key Выбранный индекс
key_len key_length Длина выбранного ключа
ref ref Столбцы, сравниваемые с индексом
rows rows Ожидаемое количество строк для проверки
filtered filtered Процент строк, отфильтрованных условием таблицы
Extra None Дополнительная информация

Примечание

Свойства JSON, которые NULL, не отображаются в выводе в формате JSON EXPLAIN.

  • id (JSON name: select_id)

    Идентификатор SELECT. Это последовательный номер SELECT в запросе. Значение может быть NULL, если строка относится к результату объединения других строк. В этом случае столбец table показывает значение, например, <unionM,N>, чтобы указать, что строка относится к объединению строк со значениями id в M и N.

  • select_type (JSON name: none)

    Тип SELECT, который может быть любым из показанных в следующей таблице. JSON-форматированный EXPLAIN раскрывает тип SELECT как свойство query_block, если он не SIMPLE или PRIMARY. В таблице также показаны имена в формате JSON (применимо).

    select_type Значение Имя в формате JSON Значение
    SIMPLE None Простой SELECT (не использует UNION или подзапросы)
    PRIMARY None Внедренный SELECT
    UNION None Второй или последующий SELECT оператор в UNION
    DEPENDENT UNION dependent (true) Второй или последующий SELECT оператор в UNION, зависит от внешнего запроса
    UNION RESULT union_result Результат UNION.
    SUBQUERY None Первый SELECT в подзапросе
    DEPENDENT SUBQUERY dependent (true) Первый SELECT в подзапросе, зависит от внешнего запроса
    DERIVED None Производная таблица
    DEPENDENT DERIVED dependent (true) Производная таблица, зависящая от другой таблицы
    MATERIALIZED materialized_from_subquery Материализованный подзапрос
    UNCACHEABLE SUBQUERY cacheable (false) Подзапрос, результат которого не может быть кэширован и должен быть перевычислен для каждой строки внешнего запроса
    UNCACHEABLE UNION cacheable (false) Второй или последующий UNION в подзапросе, который не подлежит кэшированию (см. UNCACHEABLE SUBQUERY)

    DEPENDENT обычно указывает на использование коррелированного подзапроса. См. Раздел 15.2.15.7, «Коррелированные подзапросы».

    Вычисление DEPENDENT SUBQUERY отличается от вычисления UNCACHEABLE SUBQUERY. Для DEPENDENT SUBQUERY подзапрос пересчитывается только один раз для каждого набора различных значений переменных из его внешнего контекста. Для UNCACHEABLE SUBQUERY подзапрос пересчитывается для каждой строки внешнего контекста.

    При указании FORMAT=JSON с EXPLAIN вывод не имеет единственного свойства, напрямую эквивалентного select_type; свойство query_block соответствует заданному SELECT. Доступны эквивалентные свойства большинства типов подзапросов SELECT (например, materialized_from_subquery для MATERIALIZED), и они отображаются при необходимости. Для SIMPLE и PRIMARY нет эквивалентов JSON.

    Значение select_type для операторов, отличных от SELECT, отображает тип оператора для затронутых таблиц. Например, select_type равно DELETE для операторов DELETE.

  • table (JSON name: table_name)

    Название таблицы, к которой относится строка вывода. Также это может быть одно из следующих значений:

    • <unionM,N>: Строка относится к объединению строк со значениями id в M и N.

    • <derivedN>: Строка относится к результату производной таблицы для строки со значением id в N. Производная таблица, например, может получиться из подзапроса в пункте FROM.

    • <subqueryN>: Строка относится к результату материализованного подзапроса для строки со значением id в N. См. Раздел 10.2.2.2, «Оптимизация подзапросов с помощью материализации».

  • partitions (JSON name: partitions)

    Разделы, из которых записи будут сопоставляться запросом. Значение равно NULL для неразделенных таблиц. См. Раздел 26.3.5, «Получение информации о разделах».

  • type (JSON name: access_type)

    Тип объединения. Описания разных типов см. в EXPLAIN Типы объединений.

  • possible_keys (JSON name: possible_keys)

    Столбец possible_keys указывает индексы, из которых MySQL может выбрать поиск строк в этой таблице. Обратите внимание, что этот столбец полностью независим от порядка таблиц, отображаемых в выводе EXPLAIN. Это означает, что некоторые ключи в possible_keys могут быть неприменимы на практике с заданным порядком таблиц.

    Если этот столбец NULL (или не определен в JSON-выводе), соответствующих индексов нет. В этом случае вы можете улучшить производительность запроса, проверив пункт WHERE, чтобы узнать, относится ли он к какому-либо столбцу или столбцам, подходящим для индексирования. Если да, создайте соответствующий индекс и проверьте запрос с помощью EXPLAIN еще раз. См. Раздел 15.1.9, «Оператор ALTER TABLE».

    Чтобы увидеть имеющиеся в таблице индексы, используйте SHOW INDEX FROM tbl_name.

  • key (JSON name: key)

    Столбец key указывает ключ (индекс), который MySQL фактически решил использовать. Если MySQL принимает решение использовать один из possible_keys индексов для поиска строк, этот индекс перечислен как значение ключа.

    Возможно, что key может именовать индекс, отсутствующий в значении possible_keys. Это может произойти, если ни один из possible_keys индексов не подходит для поиска строк, но все столбцы, выбранные запросом, являются столбцами другого индекса. То есть, указанный индекс охватывает выбранные столбцы, поэтому, хотя он не используется для определения строк для извлечения, сканирование индекса более эффективно, чем сканирование строки данных.

    Для InnoDB вторичный индекс может охватывать выбранные столбцы, даже если запрос также выбирает первичный ключ, потому что InnoDB хранит значение первичного ключа с каждым вторичным индексом. Если key равно NULL, MySQL не нашел индекс, который можно было бы использовать для более эффективного выполнения запроса.

    Чтобы принудительно заставить MySQL использовать или игнорировать индекс, указанный в столбце possible_keys, используйте FORCE INDEX, USE INDEX или IGNORE INDEX в вашем запросе. См. Раздел 10.9.4, «Подсказки индексов».

    Для MyISAM таблиц запуск ANALYZE TABLE помогает оптимизатору выбирать лучшие индексы. Для MyISAM таблиц myisamchk --analyze делает то же самое. См. Раздел 15.7.3.1, «Оператор ANALYZE TABLE» и Раздел 9.6, «Техническое обслуживание и восстановление после сбоев таблиц MyISAM».

  • key_len (JSON name: key_length)

    Столбец key_len указывает длину ключа, которую MySQL решил использовать. Значение key_len позволяет определить, сколько частей составного ключа MySQL фактически использует. Если в столбце key указано NULL, то в столбце key_len также указано NULL.

    Из-за формата хранения ключа длина ключа на один больше для столбца, который может быть NULL, чем для столбца NOT NULL.

  • ref (JSON name: ref)

    Столбец ref показывает, какие столбцы или константы сравниваются с индексом, указанным в столбце key, для выбора строк из таблицы.

    Если значение равно func, то используемое значение является результатом некоторой функции. Чтобы увидеть, какая функция, используйте SHOW WARNINGS после EXPLAIN, чтобы увидеть расширенный EXPLAIN вывод. Функция может быть фактически оператором, таким как арифметический оператор.

  • rows (JSON name: rows)

    Столбец rows указывает количество строк, которое, по мнению MySQL, необходимо просмотреть для выполнения запроса.

    Для таблиц InnoDB это число является оценкой и может не всегда быть точным.

  • filtered (JSON name: filtered)

    Столбец filtered указывает приблизительный процент строк таблицы, отфильтрованных условием таблицы. Максимальное значение равно 100, что означает, что не произошло фильтрации строк. Уменьшение значений от 100 указывает на увеличение объёма фильтрации. rows показывает количество оценённых просмотренных строк, а rows × filtered показывает количество строк, которые объединяются со следующей таблицей. Например, если rows равен 1000, а filtered равен 50,00 (50%), то количество строк для объединения со следующей таблицей составляет 1000 × 50% = 500.

  • Extra (JSON name: none)

    Этот столбец содержит дополнительную информацию о том, как MySQL решает запрос. Для описаний различных значений см. EXPLAIN Дополнительная информация.

    Нет единственного JSON свойства, соответствующего столбцу Extra; однако значения, которые могут встречаться в этом столбце, представлены как JSON свойства или как текст свойства message.

Объяснение типов соединения

Столбец type вывода EXPLAIN описывает, как соединяются таблицы. В выводе в формате JSON они находятся в качестве значений свойства access_type. В следующем списке описаны типы соединений, упорядоченные от лучшего типа к худшему:

  • system

    Таблица содержит только одну строку (= системная таблица). Это частный случай типа соединения const.

  • const

    Таблица содержит не более одной совпадающей строки, которая считывается в начале запроса. Поскольку существует только одна строка, значения из столбца в этой строке могут рассматриваться как константы остальной частью оптимизатора. Таблицы const очень быстрые, поскольку они считываются только один раз.

    const используется, когда вы сравниваете все части индекса PRIMARY KEY или UNIQUE с константными значениями. В следующих запросах tbl_name может использоваться как таблица const:

    SELECT * FROM tbl_name WHERE primary_key=1;
    
    SELECT * FROM tbl_name
      WHERE primary_key_part1=1 AND primary_key_part2=2;
    
  • eq_ref

    Для каждой комбинации строк из предыдущих таблиц из этой таблицы считывается одна строка. За исключением типов system и const, это наилучший возможный тип соединения. Он используется, когда все части индекса используются соединением и индекс является индексом PRIMARY KEY или UNIQUE NOT NULL.

    eq_ref может использоваться для индексированных столбцов, которые сравниваются с использованием оператора =. Значение сравнения может быть константой или выражением, использующим столбцы из таблиц, которые считываются до этой таблицы. В следующих примерах MySQL может использовать соединение eq_ref для обработки ref_table:

    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;
    
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1=other_table.column
      AND ref_table.key_column_part2=1;
    
  • ref

    Все строки с совпадающими значениями индекса считываются из этой таблицы для каждой комбинации строк из предыдущих таблиц. ref используется, если соединение использует только самый левый префикс ключа или если ключ не является индексом PRIMARY KEY или UNIQUE (другими словами, если соединение не может выбрать одну строку на основе значения ключа). Если используемый ключ соответствует только нескольким строкам, это хороший тип соединения.

    ref может использоваться для индексированных столбцов, которые сравниваются с использованием оператора = или <=>. В следующих примерах MySQL может использовать соединение ref для обработки ref_table:

    SELECT * FROM ref_table WHERE key_column=expr;
    
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;
    
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1=other_table.column
      AND ref_table.key_column_part2=1;
    
  • fulltext

    Соединение выполняется с использованием индекса FULLTEXT.

  • ref_or_null

    Этот тип соединения похож на ref, но с добавлением того, что MySQL выполняет дополнительный поиск строк, содержащих значения NULL. Эта оптимизация типа соединения чаще всего используется при разрешении подзапросов. В следующих примерах MySQL может использовать соединение ref_or_null для обработки ref_table:

    SELECT * FROM ref_table
      WHERE key_column=expr OR key_column IS NULL;
    

    См. Раздел 10.2.1.15, «Оптимизация IS NULL».

  • index_merge

    Этот тип соединения указывает на то, что используется оптимизация Index Merge. В этом случае столбец key в выходной строке содержит список используемых индексов, а key_len содержит список самых длинных частей ключей для используемых индексов. Для получения дополнительной информации см. Раздел 10.2.1.3, «Оптимизация Index Merge».

  • unique_subquery

    Этот тип заменяет eq_ref для некоторых подзапросов IN следующего вида:

    value IN (SELECT primary_key FROM single_table WHERE some_expr)
    

    unique_subquery — это просто функция поиска по индексу, которая полностью заменяет подзапрос для повышения эффективности.

  • index_subquery

    Этот тип соединения похож на unique_subquery. Он заменяет подзапросы IN, но работает для не уникальных индексов в подзапросах следующего вида:

    value IN (SELECT key_column FROM single_table WHERE some_expr)
    
  • range

    Извлекаются только строки, находящиеся в заданном диапазоне, с использованием индекса для выбора строк. Столбец key в выходной строке указывает, какой индекс используется. key_len содержит самую длинную часть ключа, которая использовалась. Столбец ref для этого типа имеет значение NULL.

    range может использоваться, когда столбец ключа сравнивается с константой с использованием любого из операторов =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE или IN():

    SELECT * FROM tbl_name
      WHERE key_column = 10;
    
    SELECT * FROM tbl_name
      WHERE key_column BETWEEN 10 and 20;
    
    SELECT * FROM tbl_name
      WHERE key_column IN (10,20,30);
    
    SELECT * FROM tbl_name
      WHERE key_part1 = 10 AND key_part2 IN (10,20,30);
    
  • index

    Тип соединения index аналогичен ALL, за исключением того, что выполняется сканирование дерева индексов. Это происходит двумя способами:

    • Если индекс является покрывающим индексом для запросов и может использоваться для удовлетворения всех необходимых данных из таблицы, сканируется только дерево индексов. В этом случае столбец Extra содержит значение Using index. Сканирование только индекса обычно быстрее, чем ALL, поскольку размер индекса обычно меньше, чем размер данных таблицы.

    • Выполняется полное сканирование таблицы с использованием считывания из индекса для поиска строк данных в порядке индекса. Uses index не отображается в столбце Extra.

    MySQL может использовать этот тип соединения, когда запрос использует только столбцы, которые являются частью одного индекса.

  • ALL

    Для каждой комбинации строк из предыдущих таблиц выполняется полное сканирование таблицы. Это обычно не хорошо, если таблица является первой таблицей, не помеченной const, и обычно очень плохо во всех остальных случаях. Обычно вы можете избежать ALL, добавив индексы, которые позволяют извлекать строки из таблицы на основе константных значений или значений столбцов из предыдущих таблиц.

Дополнительная информация EXPLAIN

Столбец Extra вывода EXPLAIN содержит дополнительную информацию о том, как MySQL обрабатывает запрос. В следующем списке объясняются значения, которые могут отображаться в этом столбце. В каждом элементе также указывается для вывода в формате JSON, какое свойство отображает значение Extra. Для некоторых из них есть определенное свойство. Остальные отображаются как текст свойства message.

Если вы хотите сделать ваши запросы максимально быстрыми, обратите внимание на значения столбца Extra равные Using filesort и Using temporary, или, в выводе в формате JSON EXPLAIN, на свойства using_filesort и using_temporary_table, равные true.

  • Backward index scan (JSON: backward_index_scan)

    Оптимизатор может использовать обратный индекс в таблице InnoDB. Отображается вместе с Using index. Дополнительная информация в разделе Раздел 10.3.13, «Обратные индексы».

  • Child of 'table' pushed join@1 (JSON: message текст)

    Эта таблица указана как дочерняя для table в объединении, которое может быть перенесено в ядро NDB. Применяется только в кластере NDB, если включены переносимые объединения. Смотрите описание системной переменной сервера ndb_join_pushdown для получения дополнительной информации и примеров.

  • const row not found (JSON свойство: const_row_not_found)

    Для запроса, такого как SELECT ... FROM tbl_name, таблица была пустой.

  • Deleting all rows (JSON свойство: message)

    Для DELETE, некоторые типы хранилищ (такие как MyISAM) поддерживают метод обработчика, который удаляет все строки таблицы простым и быстрым способом. Это значение Extra отображается, если движок использует эту оптимизацию.

  • Distinct (JSON свойство: distinct)

    MySQL ищет уникальные значения, поэтому прекращает поиск дополнительных строк для текущей комбинации строк после обнаружения первой соответствующей строки.

  • FirstMatch(tbl_name) (JSON свойство: first_match)

    Для tbl_name используется стратегия сокращения объединения FirstMatch.

  • Full scan on NULL key (JSON свойство: message)

    Это происходит при оптимизации подзапросов в качестве стратегии по умолчанию, когда оптимизатор не может использовать метод доступа с помощью индекса.

  • Impossible HAVING (JSON свойство: message)

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

  • Impossible WHERE (JSON свойство: message)

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

  • Impossible WHERE noticed after reading const tables (JSON свойство: message)

    MySQL прочитал все таблицы const (и system) и заметил, что оператор WHERE всегда ложный.

  • LooseScan(m..n) (JSON свойство: message)

    Используется стратегия LooseScan для полуобъединения. m и n — ключевые номера деталей.

  • No matching min/max row (JSON свойство: message)

    Ни одна строка не удовлетворяет условию для запроса, такого как SELECT MIN(...) FROM ... WHERE condition.

  • no matching row in const table (JSON свойство: message)

    Для запроса с объединением была пустая таблица или таблица без строк, удовлетворяющих условию уникального индекса.

  • No matching rows after partition pruning (JSON свойство: message)

    Для DELETE или UPDATE оптимизатор не обнаружил ничего для удаления или обновления после обрезки раздела. Это аналогично значению Impossible WHERE для операторов SELECT.

  • No tables used (JSON свойство: message)

    Запрос не содержит оператор FROM или содержит оператор FROM DUAL.

    Для операторов INSERT или REPLACE EXPLAIN отображает это значение, когда нет части SELECT. Например, это появляется для EXPLAIN INSERT INTO t VALUES(10), так как это эквивалентно EXPLAIN INSERT INTO t SELECT 10 FROM DUAL.

  • Not exists (JSON свойство: message)

    MySQL смог выполнить оптимизацию LEFT JOIN для запроса и не исследует больше строк в этой таблице для предыдущей комбинации строк после нахождения одной строки, соответствующей критериям LEFT JOIN. Вот пример типа запроса, который можно оптимизировать таким образом:

    SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
      WHERE t2.id IS NULL;
    

    Предположим, что t2.id определено как NOT NULL. В этом случае MySQL сканирует t1 и ищет строки в t2 с использованием значений t1.id. Если MySQL находит соответствующую строку в t2, он понимает, что t2.id никогда не может быть NULL, и не сканирует остальные строки в t2, имеющие то же значение id. Другими словами, для каждой строки в t1 MySQL должен выполнить только один поиск в t2, независимо от того, сколько строк фактически соответствует в t2.

    Это также может указывать на то, что условие WHERE формы NOT IN (subquery) или NOT EXISTS (subquery) было преобразовано во внутреннее антиобъединение. Это удаляет подзапрос и вносит его таблицы в план для самого верхнего запроса, обеспечивая улучшенное планирование стоимости. Объединяя полуобъединения и антиобъединения, оптимизатор может более свободно переупорядочивать таблицы в плане выполнения, в некоторых случаях создавая более быстрый план.

    Вы можете увидеть, когда выполняется преобразование антиобъединения для данного запроса, проверив столбец Message из SHOW WARNINGS после выполнения EXPLAIN или в выводе EXPLAIN FORMAT=TREE.

    Примечание

    Антиобъединение — это дополнение к полуобъединению table_a JOIN table_b ON condition. Антиобъединение возвращает все строки из table_a, для которых нет строки в table_b, которая соответствует condition.

  • Plan isn't ready yet (JSON свойство: none)

    Это значение появляется с EXPLAIN FOR CONNECTION, когда оптимизатор не завершил создание плана выполнения для оператора, выполняющегося в указанном подключении. Если вывод плана выполнения содержит несколько строк, любая или все из них могут иметь это значение Extra, в зависимости от прогресса оптимизатора при определении полного плана выполнения.

  • Range checked for each record (index map: N) (JSON свойство: message)

    MySQL не нашел подходящего индекса, но обнаружил, что некоторые индексы могут быть использованы после того, как станут известны значения столбцов из предыдущих таблиц. Для каждой комбинации строк в предыдущих таблицах MySQL проверяет, можно ли использовать метод доступа range или index_merge для извлечения строк. Это не очень быстро, но быстрее, чем выполнение объединения без индекса вообще. Критерии применимости описаны в Разделе 10.2.1.2, «Оптимизация диапазонов» и Разделе 10.2.1.3, «Оптимизация слияния индексов», за исключением того, что все значения столбцов для предыдущей таблицы известны и считаются константами.

    Индексы пронумерованы, начиная с 1, в том же порядке, что и в SHOW INDEX для таблицы. Значение карты индексов N — значение маски бита, указывающее, какие индексы являются кандидатами. Например, значение 0x19 (двоичное 11001) означает, что индексы 1, 4 и 5 рассматриваются.

  • Recursive (JSON свойство: recursive)

    Это указывает, что строка относится к рекурсивной части оператора SELECT в рекурсивном общем табличном выражении. См. Раздел 15.2.20, «WITH (Общие табличные выражения)».

  • Rematerialize (JSON свойство: rematerialize)

    Rematerialize (X,...) отображается в строке EXPLAIN для таблицы T, где X — любая латеральная производная таблица, рематериалзация которой запускается при чтении новой строки T. Например:

    SELECT
      ...
    FROM
      t,
      LATERAL (derived table that refers to t) AS dt
    ...
    

    Содержимое производной таблицы перематериалзуется, чтобы привести его в актуальное состояние каждый раз, когда новая строка t обрабатывается основным запросом.

  • Scanned N databases (JSON свойство: message)

    Это показывает, сколько сканирований каталога выполняет сервер при обработке запроса для таблиц INFORMATION_SCHEMA, как описано в Разделе 10.2.3, «Оптимизация запросов INFORMATION_SCHEMA». Значение N может быть 0, 1 или all.

  • Select tables optimized away (JSON property: message)

    Оптимизатор определил, что 1) должно быть возвращено не более одной строки и 2) для создания этой строки необходимо прочитать детерминированный набор строк. Когда строки, которые нужно прочитать, можно прочитать на стадии оптимизации (например, считывая строки индекса), нет необходимости читать какие-либо таблицы во время выполнения запроса.

    Первое условие выполняется, когда запрос неявно сгруппирован (содержит агрегатную функцию, но не содержит клаузу GROUP BY). Второе условие выполняется, когда по каждому используемому индексу выполняется поиск одной строки. Количество прочитанных индексов определяет количество строк для чтения.

    Рассмотрим следующий неявно сгруппированный запрос:

    SELECT MIN(c1), MIN(c2) FROM t1;
    

    Предположим, что MIN(c1) можно получить, прочитав одну строку индекса, а MIN(c2) можно получить, прочитав одну строку из другого индекса. То есть для каждого столбца c1 и c2 существует индекс, где столбец является первым столбцом индекса. В этом случае возвращается одна строка, созданная путём чтения двух детерминированных строк.

    Это значение Extra не возникает, если строки для чтения не детерминированы. Рассмотрим этот запрос:

    SELECT MIN(c2) FROM t1 WHERE c1 <= 10;
    

    Предположим, что (c1, c2) является покрывающим индексом. Используя этот индекс, для поиска минимального значения c2 необходимо просканировать все строки с c1 <= 10. В отличие от этого, рассмотрим этот запрос:

    SELECT MIN(c2) FROM t1 WHERE c1 = 10;
    

    В этом случае первая строка индекса с c1 = 10 содержит минимальное значение c2. Для получения возвращаемой строки необходимо прочитать только одну строку.

    Для систем хранения, которые поддерживают точное количество строк на таблицу (например, MyISAM, но не InnoDB), это значение Extra может возникнуть для запросов COUNT(*), для которых клаузы WHERE отсутствуют или всегда истинны, и нет клаузы GROUP BY. (Это пример неявно сгруппированного запроса, где система хранения влияет на то, можно ли прочитать детерминированное количество строк.)

  • Skip_open_table, Open_frm_only, Open_full_table (JSON property: message)

    Эти значения указывают на оптимизации открытия файлов, которые применяются к запросам для таблиц INFORMATION_SCHEMA.

    • Skip_open_table: Файлы таблиц открывать не нужно. Информация уже доступна из словаря данных.

    • Open_frm_only: Для получения информации о таблице необходимо прочитать только словарь данных.

    • Open_full_table: Неоптимизированный поиск информации. Информация о таблице должна быть прочитана из словаря данных и из файлов таблицы.

  • Start temporary, End temporary (JSON property: message)

    Это указывает на использование временных таблиц для стратегии semijoin Duplicate Weedout.

  • unique row not found (JSON property: message)

    Для запроса, такого как SELECT ... FROM tbl_name, ни одна строка не удовлетворяет условию для индекса UNIQUE или PRIMARY KEY в таблице.

  • Using filesort (JSON property: using_filesort)

    MySQL должен выполнить дополнительный проход, чтобы выяснить, как получить строки в отсортированном порядке. Сортировка выполняется путём просмотра всех строк в соответствии с типом соединения и хранения ключа сортировки и указателя на строку для всех строк, которые соответствуют клаузе WHERE. Затем ключи сортируются, и строки извлекаются в отсортированном порядке. См. Раздел 10.2.1.16, «Оптимизация ORDER BY».

  • Using index (JSON property: using_index)

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

    Для таблиц InnoDB с пользовательским кластеризованным индексом, этот индекс может использоваться даже в случае отсутствия Using index в столбце Extra. Это так, если type является index и key является PRIMARY.

    Информация обо всех используемых покрывающих индексах отображается для EXPLAIN FORMAT=TRADITIONAL и EXPLAIN FORMAT=JSON. Она также отображается для EXPLAIN FORMAT=TREE.

  • Using index condition (JSON property: using_index_condition)

    Таблицы считываются путём доступа к кортежам индексов и их проверки для определения необходимости чтения полных строк таблицы. Таким образом, информация индекса используется для отсрочки (“сдвиг вниз”) чтения полных строк таблицы, если это не требуется. См. Раздел 10.2.1.6, «Оптимизация сдвигом вниз условий индекса».

  • Using index for group-by (JSON property: using_index_for_group_by)

    Подобно методу доступа к таблицам Using index, Using index for group-by указывает, что MySQL нашёл индекс, который может использоваться для извлечения всех столбцов запроса GROUP BY или DISTINCT без дополнительных обращений к диску к фактической таблице. Кроме того, индекс используется наиболее эффективным образом, так что для каждой группы считывается только несколько записей индекса. Для подробностей см. Раздел 10.2.1.17, «Оптимизация GROUP BY».

  • Using index for skip scan (JSON property: using_index_for_skip_scan)

    Указывает, что используется метод доступа Skip Scan. См. Skip Scan Range Access Method.

  • Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access), Using join buffer (hash join) (JSON property: using_join_buffer)

    Таблицы из предыдущих соединений считываются частями в буфер соединения, а затем их строки используются из буфера для выполнения соединения с текущей таблицей. (Block Nested Loop) указывает использование алгоритма Block Nested-Loop, (Batched Key Access) указывает использование алгоритма Batched Key Access, а (hash join) указывает использование соединения с хешем. То есть ключи из таблицы на строке перед строкой EXPLAIN вывода буферизуются, а соответствующие строки извлекаются порциями из таблицы, представленной строкой, в которой появляется Using join buffer.

    В JSON-форматированном выводе значение using_join_buffer всегда является одним из Block Nested Loop, Batched Key Access или hash join.

    Для получения дополнительной информации о соединениях с хешем см. Раздел 10.2.1.4, «Оптимизация соединения с хешем».

    См. Соединения с использованием алгоритма Batched Key Access для получения информации об алгоритме Batched Key Access.

  • Using MRR (JSON property: message)

    Таблицы считываются с помощью стратегии оптимизации Multi-Range Read. См. Раздел 10.2.1.11, «Оптимизация Multi-Range Read».

  • Using sort_union(...), Using union(...), Using intersect(...) (JSON property: message)

    Эти значения указывают на конкретный алгоритм, демонстрирующий, как объединяются сканирования индексов для типа соединения index_merge. См. Раздел 10.2.1.3, «Оптимизация слияния индексов».

  • Using temporary (JSON property: using_temporary_table)

    Для решения запроса MySQL необходимо создать временную таблицу для хранения результата. Это обычно происходит, если запрос содержит клаузы GROUP BY и ORDER BY, которые перечисляют столбцы по-разному.

  • Using where (JSON property: attached_condition)

    Клауза WHERE используется для ограничения строк, которые будут сопоставлены с следующей таблицей или отправлены клиенту. Если вы не намерены извлекать или проверять все строки из таблицы, возможно, есть ошибка в вашем запросе, если значение Extra не равно Using where, а тип соединения таблиц равен ALL или index.

    Using where не имеет прямого аналога в JSON-выводе; свойство attached_condition содержит любое условие WHERE.

  • Using where with pushed condition (JSON property: message)

    Этот пункт относится NDB таблицам только. Это означает, что NDB Cluster использует оптимизацию Condition Pushdown для повышения эффективности прямого сравнения неиндексированного столбца и константы. В таких случаях условие “сдвигается вниз” до узлов данных кластера и оценивается на всех узлах данных одновременно. Это исключает необходимость отправки несоответствующих строк по сети и может ускорить такие запросы в 5-10 раз по сравнению со случаями, когда оптимизация Condition Pushdown могла бы быть, но не используется. Для получения дополнительной информации см. Раздел 10.2.1.5, «Оптимизация сдвигом вниз условия движка».

  • Zero limit (JSON property: message)

    В запросе была клауза LIMIT 0 и он не может выбрать ни одной строки.

Интерпретация вывода EXPLAIN

Хорошую оценку эффективности соединения можно получить, перемножив значения в столбце rows результата выполнения EXPLAIN. Это примерно указывает, сколько строк MySQL необходимо проверить для выполнения запроса. Если вы ограничите запросы с помощью системной переменной max_join_size, это произведение строк также используется для определения, какие многотабличные SELECT операторы выполнять, а какие отклонить. См. Раздел 7.1.1, «Настройка сервера».

Следующий пример показывает, как многотабличное соединение можно оптимизировать поэтапно, используя информацию, предоставляемую EXPLAIN.

Предположим, у вас есть SELECT оператор, показанный здесь, и вы планируете проанализировать его с помощью EXPLAIN:

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
               tt.ProjectReference, tt.EstimatedShipDate,
               tt.ActualShipDate, tt.ClientID,
               tt.ServiceCodes, tt.RepetitiveID,
               tt.CurrentProcess, tt.CurrentDPPerson,
               tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
               et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
          AND tt.ActualPC = et.EMPLOYID
          AND tt.AssignedPC = et_1.EMPLOYID
          AND tt.ClientID = do.CUSTNMBR;

Для этого примера сделайте следующие предположения:

  • Столбцы, которые сравниваются, объявлены следующим образом.

    Таблица Столбец Тип данных
    tt ActualPC CHAR(10)
    tt AssignedPC CHAR(10)
    tt ClientID CHAR(10)
    et EMPLOYID CHAR(15)
    do CUSTNMBR CHAR(15)
  • Таблицы имеют следующие индексы.

    Таблица Индекс
    tt ActualPC
    tt AssignedPC
    tt ClientID
    et EMPLOYID (ключевой)
    do CUSTNMBR (ключевой)
  • Значения tt.ActualPC распределены неравномерно.

Изначально, до проведения каких-либо оптимизаций, оператор EXPLAIN генерирует следующую информацию:

table type possible_keys key  key_len ref  rows  Extra
et    ALL  PRIMARY       NULL NULL    NULL 74
do    ALL  PRIMARY       NULL NULL    NULL 2135
et_1  ALL  PRIMARY       NULL NULL    NULL 74
tt    ALL  AssignedPC,   NULL NULL    NULL 3872
           ClientID,
           ActualPC
      Range checked for each record (index map: 0x23)

Поскольку type является ALL для каждой таблицы, этот вывод указывает, что MySQL генерирует декартово произведение всех таблиц; то есть, каждую комбинацию строк. Это занимает довольно много времени, потому что необходимо проверить произведение числа строк в каждой таблице. В данном случае это произведение равно 74 × 2135 × 74 × 3872 = 45 268 558 720 строк. Если бы таблицы были больше, можно только представить, как долго это займет.

Одна из проблем заключается в том, что MySQL может более эффективно использовать индексы по столбцам, если они объявлены одинакового типа и размера. В этом контексте, VARCHAR и CHAR считаются одинаковыми, если они объявлены одинакового размера. tt.ActualPC объявлен как CHAR(10), а et.EMPLOYID — как CHAR(15), поэтому есть несоответствие в длине.

Для исправления этого несоответствия длин столбцов используйте ALTER TABLE для увеличения длины ActualPC с 10 символов до 15:

mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

Теперь tt.ActualPC и et.EMPLOYID оба VARCHAR(15). Выполнение оператора EXPLAIN снова дает такой результат:

table type   possible_keys key     key_len ref         rows    Extra
tt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using
             ClientID,                                         where
             ActualPC
do    ALL    PRIMARY       NULL    NULL    NULL        2135
      Range checked for each record (index map: 0x1)
et_1  ALL    PRIMARY       NULL    NULL    NULL        74
      Range checked for each record (index map: 0x1)
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC 1

Это не идеально, но значительно лучше: произведение значений rows уменьшилось в 74 раза. Эта версия выполняется за несколько секунд.

Второе изменение может быть внесено для устранения несоответствий длин столбцов для сравнений tt.AssignedPC = et_1.EMPLOYID и tt.ClientID = do.CUSTNMBR:

mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
                      MODIFY ClientID   VARCHAR(15);

После этого изменения EXPLAIN выдаст результат, показанный здесь:

table type   possible_keys key      key_len ref           rows Extra
et    ALL    PRIMARY       NULL     NULL    NULL          74
tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using
             ClientID,                                         where
             ActualPC
et_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY  15      tt.ClientID   1

На этом этапе запрос оптимизирован почти максимально. Остается проблема, что по умолчанию MySQL предполагает, что значения в столбце tt.ActualPC распределены равномерно, а для таблицы tt это не так. К счастью, легко сказать MySQL проанализировать распределение ключей:

mysql> ANALYZE TABLE tt;

С дополнительной информацией об индексе соединение становится идеальным, и EXPLAIN дает такой результат:

table type   possible_keys key     key_len ref           rows Extra
tt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using
             ClientID,                                        where
             ActualPC
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1
et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY 15      tt.ClientID   1

Столбец rows в выводе от EXPLAIN — это обоснованное предположение оптимизатора соединений MySQL. Проверьте, близки ли числа к истине, сравнив произведение rows с фактическим количеством строк, возвращаемых запросом. Если числа сильно отличаются, вы можете получить лучшую производительность, используя STRAIGHT_JOIN в вашем SELECT операторе и пытаясь изменить порядок перечисления таблиц в условии FROM. (Однако, STRAIGHT_JOIN может предотвратить использование индексов, потому что он отключает преобразования полусоединения. См. .)

В некоторых случаях возможно выполнить операторы, изменяющие данные, когда EXPLAIN SELECT используется с подзапросом; для получения дополнительной информации см. Раздел 15.2.15.8, «Вычисленные таблицы».

© 2025 Oracle
Licensed under the GPLv2 License.
https://docs.oracle.com/cd/E17952_01/mysql-8.4-en/explain-output.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API