MySQL 查詢優化核心在於「看懂執行計劃、建對索引、寫好 SQL」。本文系統講解 EXPLAIN 輸出各欄位含義、select_type 八大類型、type 訪問層級、索引優化實戰技巧與常見陷阱。
EXPLAIN SELECT * FROM car_info WHERE name LIKE '凱迪拉克%';
| 欄位 | 含義 | 優化關注點 |
|---|---|---|
| id | 查詢序號,包含子查詢時遞增 | 相同 id 從上到下執行,id 越大優先級越高 |
| select_type | 查詢類型 (見下表) | 識別子查詢、UNION、衍生表等 |
| table | 訪問的表 (或衍生表 | 關注驅動表順序 |
| type | 訪問類型:system > const > eq_ref > ref > range > index > ALL | 核心指標,避免 ALL/index,爭取 ref/range/const |
| possible_keys | 理論可用索引 | 為 NULL 需建索引 |
| key | 實際使用索引 | 與預期不符用 FORCE/USE/IGNORE INDEX 干預 |
| key_len | 使用索引長度 (Bytes) | 越短越好,檢查字符集/多欄位是否全用上 |
| ref | 與索引比較的列/常數 | const/func/列名 |
| rows | 預估掃描行數 | 越小越好,配合 WHERE 條件下推 |
| Extra | 額外資訊 | Using index (覆蓋) 好;Using where 普;Using temporary/Using filesort 差 |
| 類型 | 含義 | SQL 範例 |
|---|---|---|
| SIMPLE | 無子查詢/UNION 的簡單查詢 | SELECT * FROM car_info; |
| PRIMARY | 最外層查詢 | SELECT * FROM (SELECT name FROM car_info) a; → 外層 id=1 PRIMARY |
| UNION | UNION 中第二個及之後的 SELECT | SELECT ... UNION SELECT ... → 第二個 UNION |
| DEPENDENT UNION | UNION 中依賴外部查詢的 SELECT | SELECT * FROM t1 WHERE id IN (SELECT ... UNION SELECT ...) |
| UNION RESULT | UNION 合併結果 | id 為 NULL,table= |
| SUBQUERY | 子查詢中第一個 SELECT | SELECT * FROM t1 WHERE id = (SELECT id FROM t2) → 子查詢 SUBQUERY |
| DEPENDENT SUBQUERY | 子查詢依賴外部查詢 (如 IN 子查詢) | SELECT * FROM t1 WHERE id IN (SELECT id FROM t2) → 子查詢 DEPENDENT SUBQUERY |
| DERIVED | FROM 子查詢衍生表 (物化到臨時表) | SELECT * FROM (SELECT * FROM t1) a → 衍生表 DERIVED |
ALL (全表掃描)
→ index (全索引掃描)
→ range (索引範圍掃描)
→ ref (非唯一索引等值查找)
→ eq_ref (唯一索引/主鍵等值查找)
→ const/system (常數/系統表,最多 1 行)
→ NULL (優化階段已得結果,無需訪問表)
BETWEEN/IN/>//LIKE 'prefix%' 走索引範圍WHERE idx_col = 'val'WHERE pk = 1,查詢優化為常數EXPLAIN SELECT MIN(id) FROM t,優化器直接讀統計資訊WHERE phone = 13800000000 字符串欄位傳數字 → 全表掃描)。WHERE YEAR(create_time) = 2023 → 全表掃描。改寫 WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01'。WHERE name LIKE '%abc' 無法走索引。若必須模糊前綴,考慮全文索引或反轉存儲。INT UNSIGNED、狀態碼用 TINYINT。TINYINT 夠用別用 INT,VARCHAR(20) 夠用別用 VARCHAR(255),減少 IO/記憶體/索引大小。SELECT * FROM t PROCEDURE ANALYSE(16,256); 建議 ENUM 化欄位 (基數≤16、長度≤256)。INDEX idx_content(content(100))),全文搜尋用 FULLTEXT。-- 強制使用
SELECT * FROM t FORCE INDEX (idx_name) WHERE name = 'abc';
-- 忽略索引
SELECT * FROM t IGNORE INDEX (idx_name) WHERE name = 'abc';
-- 建議使用
SELECT * FROM t USE INDEX (idx_name) WHERE name = 'abc';
EXPLAIN 顯示 type=ALL 或 type=index → 必須優化key 為 NULL 但 possible_keys 有值 → 索引未命中,檢查隱式轉換/運算/函數Extra 含 Using temporary、Using filesort → 索引順序不符合 ORDER BY/GROUP BY,調整索引欄位順序rows 極大 (百萬+) → 檢查 WHERE 條件是否下推、分區裁剪是否生效熟練掌握 EXPLAIN 語言,像醫生讀片子一樣診斷 SQL 健康狀態,配合索引設計原則與 SQL 改寫技巧,解決 90% 的查詢效能問題。