EXPLAIN показує план виконання запиту, не виконуючи його. Кожен рядок виводу - одна таблиця в запиті, у порядку, в якому MySQL їх читає.
EXPLAIN SELECT * FROM orders WHERE user_id = 7 AND status = 'paid';
Найважливіші колонки:
type- спосіб доступу. Від найкращого до найгіршого:const/system- щонайбільше один рядок за первинним чи унікальним ключем;eq_ref- у з'єднанні один рядок цієї таблиці на кожен рядок попередньої (за унікальним ключем);ref- пошук за неунікальним індексом з рівністю;range- діапазон індексу (BETWEEN,>,IN,LIKE 'abc%');index- повне сканування індексу (дешевше за таблицю, але все одно все);ALL- повне сканування таблиці. На великій таблиці - головний сигнал проблеми.
possible_keys- індекси, які теоретично підходять;key- обраний.NULLуkey- індекс не використано.key_len- скільки байтів індексу використано. Для складеного індексу показує, скільки колонок з нього реально задіяно.rows- оцінка кількості рядків, які доведеться прочитати (за статистикою, не точна).filtered- який відсоток прочитаних рядків пройде решту умов.rows × filtered / 100- скільки рядків піде далі.Extra- додаткові подробиці:Using index- запит обслуговано лише з індексу (покривний індекс), чудово;Using where- умову перевірено після читання рядка;Using index condition- частину умови перевірено ще в індексі (Index Condition Pushdown);Using filesort- потрібне окреме сортування;Using temporary- потрібна тимчасова таблиця (частоGROUP BYчиDISTINCT).
Що шукати першим:
type = ALLна великій таблиці;- великі
rowsпри маленькому результаті - індекс неселективний чи відсутній; Using filesort/Using temporaryна запитах, що мають бути швидкими.
Обмеження: EXPLAIN показує план і оцінки, а не фактичний час. Щоб побачити, скільки рядків прочитано насправді й де витрачено час, потрібен EXPLAIN ANALYZE.
У Laravel план зручно отримати прямо з будівника: User::where('email', $email)->explain()->dd();.