10.8.2 Формат вывода EXPLAIN
Оператор EXPLAIN предоставляет информацию о том, как MySQL выполняет операторы. EXPLAIN работает с операторами SELECT, DELETE, INSERT, REPLACE и UPDATE.
EXPLAIN возвращает строку информации для каждой таблицы, используемой в операторе SELECT. Он перечисляет таблицы в выводе в порядке, в котором MySQL их считывает при обработке оператора. Это означает, что MySQL считывает строку из первой таблицы, затем находит соответствующую строку во второй таблице, а затем в третьей и так далее. Когда все таблицы обработаны, MySQL выводит выбранные столбцы и возвращается к списку таблиц до тех пор, пока не найдется таблица, для которой имеется больше соответствующих строк. Следующая строка считывается из этой таблицы, и процесс продолжается с последующей таблицей.
MySQL Workbench имеет возможность визуального объяснения, которая предоставляет визуальное представление вывода EXPLAIN. См. .
Столбцы вывода EXPLAIN
В этом разделе описаны столбцы вывода, полученные оператором EXPLAIN. В последующих разделах содержится дополнительная информация о столбцах type и Extra.
Каждая строка вывода из EXPLAIN содержит информацию об одной таблице. Каждая строка содержит значения, суммированные в таблице 10.1 «Столбцы вывода EXPLAIN» и описанные более подробно после таблицы. Названия столбцов показаны в первом столбце таблицы; во втором столбце показано эквивалентное имя свойства, показанное в выводе, когда FORMAT=JSON используется.
Таблица 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показывает значение, например,<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 Производная таблица DEPENDENT DERIVEDdependent(true)Производная таблица, зависящая от другой таблицы MATERIALIZEDmaterialized_from_subqueryМатериализованный подзапрос UNCACHEABLE SUBQUERYcacheable(false)Подзапрос, результат которого не может быть кэширован и должен быть перевычислен для каждой строки внешнего запроса UNCACHEABLE UNIONcacheable(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)Название таблицы, к которой относится строка вывода. Также это может быть одно из следующих значений:
<union: Строка относится к объединению строк со значениямиM,N>idвMиN.<derived: Строка относится к результату производной таблицы для строки со значениемN>idвN. Производная таблица, например, может получиться из подзапроса в пунктеFROM.<subquery: Строка относится к результату материализованного подзапроса для строки со значениемN>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. В следующем списке описываются типы соединений, упорядоченные от лучшего к худшему:
-
Таблица содержит только одну строку (= системная таблица). Это частный случай типа соединения
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содержит список самых длинных частей ключей для используемых индексов. Для получения дополнительной информации см. Раздел 10.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.
-
Backward index scan(JSON:backward_index_scan)Оптимизатор может использовать индекс с убывающим порядком для таблицы
InnoDB. Показано вместе сUsing index. Для получения дополнительной информации см. Раздел 10.3.13, «Убывающие индексы». -
Child of '(JSON:table' pushed join@1messagetext)Эта таблица упоминается как дочерняя к
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((JSON property:tbl_name)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((JSON property:m..n)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. Другими словами, для каждой строки вt1MySQL должен выполнить только один поиск вt2, независимо от того, сколько строк на самом деле соответствует вt2.Это также может указывать на то, что условие
WHEREв формеNOT IN (илиsubquery)NOT EXISTS (было преобразовано внутри в антисоединение. Это удаляет подзапрос и включает его таблицы в план для самого верхнего запроса, обеспечивая улучшенное планирование стоимости. Объединяя полусоединения и антисоединения, оптимизатор может более свободно переупорядочивать таблицы в плане выполнения, в некоторых случаях приводя к более быстрому плану.subquery)Вы можете увидеть, когда выполняется преобразование антисоединения для данного запроса, проверив столбец
MessageизSHOW WARNINGSпосле выполненияEXPLAINили в выводеEXPLAIN FORMAT=TREE.ПримечаниеАнтисоединение является дополнением полусоединения
. Антисоединение возвращает все строки изtable_aJOINtable_bONconditiontable_a, для которых нет строки вtable_b, которая соответствуетcondition. -
Plan is not ready yet(JSON property: none)Это значение встречается с
EXPLAIN FOR CONNECTION, когда оптимизатор ещё не завершил создание плана выполнения для оператора, выполняющегося в именованном соединении. Если вывод плана выполнения состоит из нескольких строк, любая или все из них могут иметь это значениеExtra, в зависимости от прогресса оптимизатора в определении полного плана выполнения. -
Range checked for each record (index map:(JSON property:N)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(JSON property:Ndatabasesmessage)Это указывает на то, сколько сканирований каталогов выполняет сервер при обработке запроса для таблиц
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_nameUNIQUEили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;
Для этого примера сделайте следующие предположения:
-
Столбцы, которые сравниваются, объявлены следующим образом.
Таблица Столбец Тип данных 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 может предотвратить использование индексов, потому что он отключает преобразования полусоединения. См. .)
В некоторых случаях возможно выполнить операторы, изменяющие данные, когда EXPLAIN
SELECT используется с подзапросом; для получения дополнительной информации см. Раздел 15.2.15.8, «Вычисленные таблицы».
© 2025 Oracle
Licensed under the GPLv2 License.