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. В последующих разделах приводится дополнительная информация о столбцах type и Extra.
Каждая строка вывода EXPLAIN предоставляет информацию об одной таблице. Каждая строка содержит значения, обобщенные в таблице 8.1, «Столбцы вывода EXPLAIN», и описанные более подробно после таблицы. Имена столбцов показаны в первом столбце таблицы; второй столбец предоставляет эквивалентное имя свойства, показанное в выводе, когда используется FORMAT=JSON.
Таблица 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показывает значение, например,<union, чтобы указать, что строка относится к объединению строк со значениямиM,N>idвMиN. -
select_type(JSON name: none)Тип
SELECT, который может быть любым из показанных в следующей таблице. JSON-форматированныйEXPLAINпоказывает типSELECTкак свойствоquery_block, если он не являетсяSIMPLEилиPRIMARY. Имена в JSON-формате (если применимо) также показаны в таблице.select_typeЗначениеИмя в JSON Значение SIMPLENone Простой SELECT(не используетUNIONили подзапросы)PRIMARYNone Внешний SELECTUNIONNone Второй или последующий SELECTвUNIONDEPENDENT UNIONdependent(true)Второй или последующий SELECTвUNION, зависимый от внешнего запросаUNION RESULTunion_resultРезультат UNION.SUBQUERYNone Первый SELECTв подзапросеDEPENDENT SUBQUERYdependent(true)Первый SELECTв подзапросе, зависимый от внешнего запросаDERIVEDNone Таблица вывода MATERIALIZEDmaterialized_from_subqueryМатериализованный подзапрос UNCACHEABLE SUBQUERYcacheable(false)Подзапрос, результат которого не может быть кэширован и должен пересчитываться для каждой строки внешнего запроса UNCACHEABLE UNIONcacheable(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)Название таблицы, к которой относится строка вывода. Это также может быть одно из следующих значений:
<union: Строка относится к объединению строк со значениямиM,N>idвMиN.<derived: Строка относится к результату таблицы вывода для строки со значениемN>idвN. Таблица вывода может, например, получиться из подзапроса в предложенииFROM.<subquery: Строка относится к результату материализованного подзапроса для строки со значениемN>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. В следующем списке описаны типы соединений, упорядоченные от лучшего к худшему:
-
Таблица содержит только одну строку (= системная таблица). Это частный случай типа соединения
const. -
Таблица содержит не более одной совпадающей строки, которая считывается в начале запроса. Поскольку существует только одна строка, значения из столбца в этой строке могут рассматриваться как константы остальной частью оптимизатора. Таблицы
constочень быстрые, потому что они считываются только один раз.constиспользуется, когда вы сравниваете все части индексаPRIMARY KEYилиUNIQUEс константными значениями. В следующих запросахtbl_nameможет использоваться как таблицаconst:SELECT * FROM
tbl_nameWHEREprimary_key=1; SELECT * FROMtbl_nameWHEREprimary_key_part1=1 ANDprimary_key_part2=2; -
Для каждой комбинации строк из предыдущих таблиц из этой таблицы считывается одна строка. Помимо типов
systemиconst, это лучший возможный тип соединения. Он используется, когда все части индекса используются соединением, и индекс является индексомPRIMARY KEYилиUNIQUE NOT NULL.eq_refможет использоваться для индексированных столбцов, которые сравниваются с помощью оператора=. Значение сравнения может быть константой или выражением, использующим столбцы из таблиц, которые считываются до этой таблицы. В следующих примерах MySQL может использовать соединениеeq_refдля обработкиref_table:SELECT * FROM
ref_table,other_tableWHEREref_table.key_column=other_table.column; SELECT * FROMref_table,other_tableWHEREref_table.key_column_part1=other_table.columnANDref_table.key_column_part2=1; -
Все строки с совпадающими значениями индекса считываются из этой таблицы для каждой комбинации строк из предыдущих таблиц.
refиспользуется, если соединение использует только самый левый префикс ключа или если ключ не является индексомPRIMARY KEYилиUNIQUE(другими словами, если соединение не может выбрать одну строку на основе значения ключа). Если используемый ключ соответствует только нескольким строкам, это хороший тип соединения.refможет использоваться для индексированных столбцов, которые сравниваются с помощью оператора=или<=>. В следующих примерах MySQL может использовать соединениеrefдля обработкиref_table:SELECT * FROM
ref_tableWHEREkey_column=expr; SELECT * FROMref_table,other_tableWHEREref_table.key_column=other_table.column; SELECT * FROMref_table,other_tableWHEREref_table.key_column_part1=other_table.columnANDref_table.key_column_part2=1; -
Соединение выполняется с использованием индекса
FULLTEXT. -
Этот тип соединения похож на
ref, но с добавлением того, что MySQL выполняет дополнительный поиск строк, содержащих значенияNULL. Эта оптимизация типа соединения чаще всего используется при разрешении подзапросов. В следующих примерах MySQL может использовать соединениеref_or_nullдля обработкиref_table:SELECT * FROM
ref_tableWHEREkey_column=exprORkey_columnIS NULL; -
Этот тип соединения указывает на то, что используется оптимизация Index Merge. В этом случае столбец
keyв выходной строке содержит список используемых индексов, аkey_lenсодержит список самых длинных частей ключей для используемых индексов. Для получения дополнительной информации см. Раздел 8.2.1.3, «Оптимизация Index Merge». -
Этот тип заменяет
eq_refдля некоторых подзапросовINследующего вида:valueIN (SELECTprimary_keyFROMsingle_tableWHEREsome_expr)unique_subquery— это просто функция поиска по индексу, которая полностью заменяет подзапрос для повышения эффективности. -
Этот тип соединения аналогичен
unique_subquery. Он заменяет подзапросыIN, но работает для не уникальных индексов в подзапросах следующего вида:valueIN (SELECTkey_columnFROMsingle_tableWHEREsome_expr) -
Извлекаются только строки, находящиеся в заданном диапазоне, с использованием индекса для выбора строк. Столбец
keyв выходной строке указывает, какой индекс используется.key_lenсодержит самую длинную часть ключа, которая была использована. Столбецrefдля этого типа равенNULL.rangeможет использоваться, когда столбец ключа сравнивается с константой с помощью любого из операторов=,<>,>,>=,<,<=,IS NULL,<=>,BETWEEN,LIKEилиIN():SELECT * FROM
tbl_nameWHEREkey_column= 10; SELECT * FROMtbl_nameWHEREkey_columnBETWEEN 10 and 20; SELECT * FROMtbl_nameWHEREkey_columnIN (10,20,30); SELECT * FROMtbl_nameWHEREkey_part1= 10 ANDkey_part2IN (10,20,30); -
Тип соединения
indexаналогиченALL, за исключением того, что выполняется сканирование дерева индекса. Это происходит двумя способами:Если индекс является покрывающим индексом для запросов и может использоваться для удовлетворения всех необходимых данных из таблицы, сканируется только дерево индекса. В этом случае столбец
Extraсодержит значениеUsing index. Сканирование только индекса обычно быстрее, чемALL, поскольку размер индекса обычно меньше, чем данные таблицы.Выполняется полное сканирование таблицы с использованием считывания из индекса для поиска строк данных в порядке индекса.
Uses indexне отображается в столбцеExtra.
MySQL может использовать этот тип соединения, когда запрос использует только столбцы, являющиеся частью одного индекса.
-
Для каждой комбинации строк из предыдущих таблиц выполняется полное сканирование таблицы. Это обычно нехорошо, если таблица является первой таблицей, не помеченной как
const, и обычно очень плохо во всех остальных случаях. Обычно вы можете избежатьALL, добавив индексы, которые позволяют извлекать строки из таблицы на основе константных значений или значений столбцов из предыдущих таблиц.
Дополнительная информация EXPLAIN
Столбец Extra вывода EXPLAIN содержит дополнительную информацию о том, как MySQL обрабатывает запрос. В следующем списке объясняются значения, которые могут появляться в этом столбце. В каждом пункте также указывается, для вывода в формате JSON, какое свойство отображает значение Extra. Для некоторых из них существует отдельное свойство. Другие отображаются как текст свойства message.
Если вы хотите сделать ваши запросы как можно быстрее, обратите внимание на значения столбца Extra равные Using
filesort и Using temporary, или, в формате JSON EXPLAIN, на свойства using_filesort и using_temporary_table равные true.
-
Child of '(JSON:table' pushed join@1messageтекст)Эта таблица упоминается как дочерняя
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((JSON свойство:tbl_name)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((JSON свойство:m..n)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. Другими словами, для каждой строки вt1MySQL нужно выполнить только один поиск вt2, независимо от того, сколько строк фактически совпадает вt2. -
Plan isn't ready yet(JSON свойство: none)Это значение появляется при
EXPLAIN FOR CONNECTION, когда оптимизатор не завершил создание плана выполнения для оператора, выполняемого в именованном подключении. Если вывод плана выполнения состоит из нескольких строк, то любое или все из них могут иметь это значениеExtra, в зависимости от прогресса оптимизатора в определении полного плана выполнения. -
Range checked for each record (index map:(JSON свойство:N)message)MySQL не нашел подходящего индекса для использования, но обнаружил, что некоторые индексы могут быть использованы после того, как известны значения столбцов из предыдущих таблиц. Для каждой комбинации строк в предыдущих таблицах MySQL проверяет, можно ли использовать метод доступа
rangeилиindex_mergeдля извлечения строк. Это не очень быстро, но быстрее, чем выполнение соединения без индекса вообще. Критерии применимости описаны в разделе 8.2.1.2, «Оптимизация диапазонов» и разделе 8.2.1.3, «Оптимизация слияния индексов» с исключением, что все значения столбцов для предыдущей таблицы известны и считаются константами.Индексы пронумерованы, начиная с 1, в том же порядке, что и в
SHOW INDEXдля таблицы. Значение карты индексовN— это значение маски битов, которое указывает, какие индексы являются кандидатами. Например, значение0x19(двоично 11001) означает, что индексы 1, 4 и 5 рассматриваются. -
Scanned(JSON свойство:Ndatabasesmessage)Это указывает, сколько сканирований каталогов выполняет сервер при обработке запроса для таблиц
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_nameUNIQUEили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;
Для этого примера сделаем следующие предположения:
-
Столбцы для сравнения объявлены следующим образом.
Таблица Столбец Тип данных ttActualPCCHAR(10)ttAssignedPCCHAR(10)ttClientIDCHAR(10)etEMPLOYIDCHAR(15)doCUSTNMBRCHAR(15) -
У таблиц есть следующие индексы.
Таблица Индекс ttActualPCttAssignedPCttClientIDetEMPLOYID(ключевой)doCUSTNMBR(ключевой) Значения
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.