Spec-Zone.ru › MySQL 5.7

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

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

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

Вывод EXPLAIN включает информацию о разбиении на разделы. Кроме того, для операторов SELECT, EXPLAIN генерирует расширенную информацию, которая может быть отображена с помощью SHOW WARNINGS после выполнения EXPLAIN (см. Раздел 8.8.3, «Расширенный формат вывода EXPLAIN»).

Примечание

В более ранних версиях MySQL информация о разбиении на разделы и расширенная информация генерировались с использованием EXPLAIN PARTITIONS и EXPLAIN EXTENDED. Эти синтаксические конструкции по-прежнему распознаются для обеспечения обратной совместимости, но вывод о разбиении на разделы и расширенный вывод теперь включены по умолчанию, поэтому ключевые слова PARTITIONS и EXTENDED избыточны и устарели. Их использование приводит к предупреждению; ожидается, что они будут удалены из синтаксиса EXPLAIN в будущих версиях MySQL.

Вы не можете использовать устаревшие ключевые слова PARTITIONS и EXTENDED вместе в одном операторе EXPLAIN. Кроме того, ни одно из этих ключевых слов не может быть использовано вместе с опцией FORMAT.

Примечание

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

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

  • Типы объединения EXPLAIN

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

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

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

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

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

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

Таблица 8.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 Таблица вывода
    MATERIALIZED materialized_from_subquery Материализованный подзапрос
    UNCACHEABLE SUBQUERY cacheable (false) Подзапрос, результат которого не может быть кэширован и должен пересчитываться для каждой строки внешнего запроса
    UNCACHEABLE UNION cacheable (false) Второй или последующий UNION в некэшируемом подзапросе (см. UNCACHEABLE SUBQUERY)

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

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

    Кэширование подзапросов отличается от кэширования результатов запросов в кэше запросов (описано в Разделе 8.10.3.1, «Как работает кэш запросов»). Кэширование подзапросов происходит во время выполнения запроса, в то время как кэш запросов используется для хранения результатов только после завершения выполнения запроса.

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

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

  • table (JSON name: table_name)

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

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

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

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

  • partitions (JSON name: partitions)

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

  • type (JSON name: access_type)

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

  • possible_keys (JSON name: possible_keys)

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

    Если этот столбец NULL (или не определен в формате JSON), релевантных индексов нет. В этом случае можно улучшить производительность запроса, изучив предложение WHERE, чтобы проверить, относится ли оно к столбцу или столбцам, подходящим для индексации. В случае положительного ответа создайте соответствующий индекс и проверьте запрос с помощью EXPLAIN ещё раз. См. Раздел 13.1.8, «Оператор 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 в запросе. См. Раздел 8.9.4, «Подсказки индексов».

    Для MyISAM таблиц выполнение ANALYZE TABLE помогает оптимизатору выбирать лучшие индексы. Для MyISAM таблиц myisamchk --analyze делает то же самое. См. Раздел 13.7.2.1, «Оператор ANALYZE TABLE», и Раздел 7.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.

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

Столбец 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;
    

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

  • index_merge

    Этот тип соединения указывает на то, что используется оптимизация Index Merge. В этом случае столбец key в выходной строке содержит список используемых индексов, а key_len содержит список самых длинных частей ключей для используемых индексов. Для получения дополнительной информации см. Раздел 8.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.

  • 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)

    Стратегия сокращения соединений FirstMatch semijoin используется для tbl_name.

  • 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 для semijoin. 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.

  • 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 для извлечения строк. Это не очень быстро, но быстрее, чем выполнение соединения без индекса вообще. Критерии применимости описаны в разделе 8.2.1.2, «Оптимизация диапазонов» и разделе 8.2.1.3, «Оптимизация слияния индексов» с исключением, что все значения столбцов для предыдущей таблицы известны и считаются константами.

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

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

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

  • Select tables optimized away (JSON свойство: message)

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

    Первое условие выполняется, когда запрос неявно сгруппирован (содержит агрегатную функцию, но не клаузу 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) — это индекс-оболочка. Используя этот индекс, все строки с c1 <= 10 должны быть просканированы, чтобы найти минимальное значение c2. В отличие от этого, рассмотрим этот запрос:

    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, как описано в разделе 8.2.3 «Оптимизация запросов INFORMATION_SCHEMA».

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

    • Open_frm_only: Нужно открыть только файл .frm таблицы.

    • Open_full_table: Поиск информации без оптимизации. Необходимо открыть файлы .frm, .MYD и .MYI.

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

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

  • unique row not found (JSON property: message)

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

  • Using filesort (JSON property: using_filesort)

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

  • Using index (JSON property: using_index)

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

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

  • Using index condition (JSON property: using_index_condition)

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

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

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

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

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

    В JSON-выводе значение using_join_buffer всегда либо Block Nested Loop, либо Batched Key Access.

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

  • Using MRR (JSON property: message)

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

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

    Они указывают на конкретный алгоритм, показывающий, как объединяются сканирования индексов для типа соединения index_merge. См. раздел 8.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 использует оптимизацию сдвига вниз условия для повышения эффективности прямого сравнения между неиндексированным столбцом и константой. В таких случаях условие “сдвигается вниз” до узлов данных кластера и оценивается на всех узлах данных одновременно. Это устраняет необходимость отправки несовпадающих строк по сети и может ускорить такие запросы в 5–10 раз по сравнению со случаями, где оптимизация сдвига вниз могла быть применена, но не была. Для получения дополнительной информации см. раздел 8.2.1.4 «Оптимизация сдвига вниз условия двигателя».

  • Zero limit (JSON property: message)

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

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

Хорошее представление о качестве соединения можно получить, перемножив значения в столбце rows вывода EXPLAIN. Это приблизительно указывает на количество строк, которые MySQL должно проверить для выполнения запроса. Если запросы ограничены переменной системы max_join_size, это произведение строк также используется для определения, какие запросы SELECT с несколькими таблицами следует выполнить, а какие прервать. См. Раздел 5.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 может помешать использованию индексов, поскольку отключает преобразования полусоединения. См. Раздел 8.2.2.1, «Оптимизация подзапросов, производных таблиц и ссылок на представления с помощью преобразований полусоединения».)

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

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

Spec-Zone.ru

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