Spec-Zone.ru › MySQL 9.2

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.

Типы соединений 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;
    

    См. Раздел 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 text)

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

  • const row not found (JSON property: const_row_not_found)

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

  • Deleting all rows (JSON property: message)

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

  • Distinct (JSON property: distinct)

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

  • FirstMatch(tbl_name) (JSON property: first_match)

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

  • Full scan on NULL key (JSON property: message)

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

  • Impossible HAVING (JSON property: message)

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

  • Impossible WHERE (JSON property: message)

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

  • Impossible WHERE noticed after reading const tables (JSON property: message)

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

  • LooseScan(m..n) (JSON property: message)

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

  • No matching min/max row (JSON property: message)

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

  • no matching row in const table (JSON property: message)

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

  • No matching rows after partition pruning (JSON property: message)

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

  • No tables used (JSON property: 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 property: 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 is not ready yet (JSON property: none)

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

  • Range checked for each record (index map: N) (JSON property: 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 property: recursive)

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

  • Rematerialize (JSON property: rematerialize)

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

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

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

  • Scanned N databases (JSON property: message)

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

  • Select tables optimized away (JSON property: 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 таблиц.

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

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

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

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

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

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

  • 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 использует оптимизацию сдвига вниз условия для повышения эффективности прямого сравнения неиндексированного столбца и константы. В таких случаях условие «сдвигается вниз» на узлы данных кластера и оценивается на всех узлах данных одновременно. Это исключает необходимость отправлять несоответствующие строки по сети и может ускорить такие запросы в 5–10 раз по сравнению с случаями, когда оптимизация сдвига вниз условия могла быть, но не использовалась. Дополнительную информацию см. в Разделе 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-9.2-en/explain-output.html

Spec-Zone.ru

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