SELECT
и других запросов
EXPLAIN
(получение информации о SELECT
)SELECT
WHERE
DISTINCT
LEFT JOIN
и RIGHT JOIN
ORDER BY
LIMIT
INSERT
UPDATE
DELETE
Buy this Reference Manual in softcover from Barnes & Noble!
EXPLAIN
(получение информации о SELECT
)EXPLAIN имя_таблицы или EXPLAIN SELECT опции_выборки
EXPLAIN имя_таблицы
является синонимом операторов DESCRIBE имя_таблицы
и SHOW COLUMNS FROM имя_таблицы
.
Если оператор SELECT
предваряется ключевым словом EXPLAIN
, MySQL сообщит о том, как будет производиться обработка SELECT
, и предоставит информацию о порядке и методе связывания таблиц.
При помощи EXPLAIN
можно выяснить, когда стоит снабдить таблицы индексами, чтобы получить более быструю выборку, использующую индексы для поиска записей. Кроме того, можно проверить, насколько удачный порядок связывания таблиц был выбран оптимизатором. Заставить оптимизатор связывать таблицы в заданном порядке можно при помощи указания STRAIGHT_JOIN
.
Для непростых соединений EXPLAIN
возвращает строку информации о каждой из использованных в работе оператора SELECT
таблиц. Таблицы перечисляются в том порядке, в котором они будут считываться. MySQL выполняет все связывания за один проход (метод называется "single-sweep multi-join"). Делается это так: MySQL читает строку из первой таблицы, находит совпадающую строку во второй таблице, затем - в третьей, и так далее. Когда обработка всех таблиц завершается, MySQL выдает выбранные столбцы и обходит в обратном порядке список таблиц до тех пор, пока не будет найдена таблица с наибольшим совпадением строк. Следующая строка считывается из этой таблицы и процесс продолжается в следующей таблице.
Вывод команды EXPLAIN
включает следующие столбцы:
table
type
possible_keys
possible_keys
служит для указания индексов, которые может использовать MySQL для нахождения строк в этой таблице. Обратите внимание: этот столбец полностью независим от порядка таблиц. Это означает, что на практике некоторые ключи в столбце possible_keys
могут не годиться для сгенерированного порядка таблиц. Если данный столбец пуст, то никаких подходящих индексов не имеется. В этом случае для увеличения производительности следует исследовать выражение WHERE
, чтобы увидеть, есть ли в нем ссылки на какой-либо столбец (столбцы), которые подходили бы для индексации. Если да, создайте соответствующий индекс и снова проверьте запрос при помощи оператора EXPLAIN
. See section 6.5.4 Синтаксис оператора ALTER TABLE
. Чтобы увидеть, какие индексы есть в таблице, используйте команду SHOW INDEX FROM имя_таблицы
.key
key
содержит ключ (индекс), который MySQL решил использовать в действительности. Если никакой индекс не был выбран, ключ будет иметь значение NULL
. Если был выбран неверный индекс, можно попробовать заставить MySQL применить другой индекс. Для этого следует использовать myisamchk -analyze
(see section 4.4.6.1 Синтаксис запуска myisamchk
) или оператор USE INDEX/IGNORE INDEX
. See section 6.4.1 Синтаксис оператора SELECT
.key_len
key_len
содержит длину ключа, которую решил использовать MySQL. Если key
имеет значение NULL
, то длина ключа (key_len
) тоже NULL
. Обратите внимание: по значению длины ключа можно определить, сколько частей составного ключа в действительности будет использовать MySQL.ref
ref
показывает, какие столбцы или константы используются с ключом, указанным в key
, для выборки строк из таблицы.rows
rows
указывается число строк, которые MySQL считает нужным проанализировать для выполнения запроса.Extra
Distinct
Not exists
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
.
range checked for each record (index map: #)
Using filesort
join type
) и сохраняются ключ сортировки + указатель на строку для всех строк, удовлетворяющих выражению WHERE
. После этого ключи сортируются и строки извлекаются в порядке сортировки.Using index
Using temporary
ORDER BY
выполняется для набора столбцов, отличного от того, который используется в предложении GROUP BY
.Where used
WHERE
будет использоваться для выделения тех строк, которые будут сопоставляться со следующей таблицей или тех, которые будут посланы клиенту. Если этой информации нет, а таблица имеет тип ALL
или index
, то, значит, в вашем запросе есть какая-то ошибка (если вы не собираетесь делать выборку/тестирование всех строк таблицы).Using filesort
и Using temporary
.
Ниже перечислены различные типы связывания, упорядоченные от лучшего к худшему:
system
const
.
const
const
являются очень быстрыми, поскольку они читаются только однажды!eq_ref
const
. Данный тип применяется, когда все части индекса используются для связывания, а сам индекс - UNIQUE
или PRIMARY KEY
.ref
ref
применяется, если для связывания используется только крайний левый префикс ключа, или если ключ не является UNIQUE
или PRIMARY KEY
(другими словами, если на основании значения ключа для связывания не может быть выбрана одна строка). Этот тип связывания хорошо работает, если используемый ключ соответствует только нескольким строкам.range
key
. Столбец key_len
содержит самую длинную часть ключа, которая была использована. Столбец ref
будет содержать значения NULL
для этого типа.index
ALL
, за исключением того, что просматривается только дерево индексов. Этот тип обычно более быстрый чем ALL
, поскольку индексный файл, как правило, меньше файла данных.ALL
const
, и очень плохо во всех остальных случаях. Как правило, можно избегать типа связывания ALL
- путем добавления большего количества индексов таким образом, чтобы строка могла быть найдена при помощи константных значений или значений столбца из предыдущих таблиц.Существует неплохой способ определить, насколько хорошим является тип связывания. Для этого нужно перемножить все значения столбца rows
, выводимого командой EXPLAIN
. Результатом будет грубая оценка того, сколько строк должен просмотреть MySQL для выполнения запроса. Это же число используется для ограничения запросов в переменной max_join_size
. See section 5.5.2 Настройка параметров сервера.
В следующем примере показано, как можно постепенно оптимизировать JOIN
при помощи информации, выводимой оператором 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 (primary key) |
do |
CUSTNMBR (primary key) |
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,ClientID,ActualPC NULL NULL NULL 3872 range checked for each record (key map: 35)
Поскольку каждая таблица имеет тип ALL
, из приведенного выше вывода видно, что MySQL будет делать полное связывание всех таблиц! Это займет долгое время, поскольку для выполнения такого связывания должно быть рассмотрено произведение числа строк в каждой таблице! Для нашего случая такое произведение - 74 * 2135 * 74 * 3872 = 45268558720 строк. Если таблицы большие, трудно даже представить себе, как долго они будут связываться.
Одна проблема здесь состоит в том, что 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,ClientID,ActualPC NULL NULL NULL 3872 where used do ALL PRIMARY NULL NULL NULL 2135 range checked for each record (key map: 1) et_1 ALL PRIMARY NULL NULL NULL 74 range checked for each record (key map: 1) et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1
Это не идеально, но уже намного лучше (произведение значений строк теперь уменьшилось в 74 раза). Такое связывание выполнится за пару секунд.
Можно сделать еще одно изменение - чтобы устранить несоответствие длин столбцов для сравнений tt.AssignedPC = et_1.EMPLOYID and 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 where used ClientID, 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 об этом можно очень просто:
shell> myisamchk --analyze PATH_TO_MYSQL_DATABASE/tt shell> mysqladmin refresh
Теперь связывание совершенно, и оператор EXPLAIN
выведет такой результат:
table type possible_keys key key_len ref rows Extra tt ALL AssignedPC NULL NULL NULL 3872 where used ClientID, 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. Чтобы оптимизировать запрос, нужно проверить, являются ли числа близкими к действительным. Если нет, можно получить лучшую производительность, используя в операторе SELECT
соединение STRAIGHT_JOIN
и попытаться задать другой порядок таблиц в выражении FROM
.
Posted by on Friday May 17 2002, @6:24am | [Delete] [Edit] |
I found that when EXPLAIN gives unexpected results
(like using another index than the one you created
especially for this goal), first running ANALYZE
TABLE on the involved tables helped a lot. I put
an ANALYZE TABLE for 10 or so "key" tables in the
nightly cronjob for this reason.
Especially useful if you mave more than one multi-
key index on a table, or if tables can be joined
along different paths.
Add your own comment.