索引是 MySQL 效能的靈魂。InnoDB 採用 B+樹作為索引結構,理解其頁面分裂、聚簇索引與二級索引雙層查找機制,能指導我們設計高效索引、避免隱式轉換、解決排序與分組性能瓶頸。本文從底層原理出發,結合 EXPLAIN 實戰案例,系統講解索引優化核心知識點。
row_id 隱藏主鍵。ALTER TABLE staffs ADD INDEX idx_nap (name, age, pos);
索引鍵值順序:(name, age, pos) 依序比較,類似字典序。
| SQL 範例 | 使用索引列 | 說明 |
|---|---|---|
WHERE name='A' AND age=25 AND pos='dev' | name, age, pos | 全值匹配 |
WHERE name='A' AND age=25 | name, age | 最左前綴 |
WHERE name='A' | name | 最左單列 |
WHERE name LIKE 'A%' | name | 前綴模糊匹配 |
WHERE name='A' AND age>25 | name, age | 精確匹配 + 範圍(範圍列之後不可用) |
SELECT name,age FROM ... WHERE name='A' | name (覆蓋索引) | 只讀索引列,無需回表 |
WHERE age=25 — 跳過最左列WHERE name='A' AND pos='dev' — 跳過中間列WHERE name>'A' AND age=25 — 範圍列在前,後列失效WHERE SUBSTR(name,1,1)='A' — 索引列加函數/運算,失效WHERE name LIKE '%A' — 非前綴模糊,全掃定義:查詢只需掃描索引即可獲得所有所需列,無需回表。
識別:EXPLAIN 的 Extra 欄顯示 Using index。
設計技巧:將 SELECT 列順序納入複合索引尾部,例如 INDEX (status, created_at, id, title, content) 覆蓋 SELECT id,title,content FROM t WHERE status=1 ORDER BY created_at。
key_len 表示本次查詢實際使用的索引位元組長度,可反推用到哪些列。
| 資料型別 | 基礎長度 | NULL 可否 | 實際 key_len |
|---|---|---|---|
| INT / MEDIUMINT | 4 / 3 | +1 | 5 / 4 |
| BIGINT | 8 | +1 | 9 |
| CHAR(N) UTF8MB4 | 3N | +1 | 3N+1 |
| VARCHAR(N) UTF8MB4 | 3N+2 | +1 | 3N+3 |
| DATE / DATETIME | 3 / 5 | +1 | 4 / 6 |
實例:INDEX idx_nap (name VARCHAR(20), age INT, pos CHAR(10)),UTF8MB4。
WHERE name='A' AND age=25 → key_len = (3*20+3) + 5 = 68,確認用了前兩列。
MySQL 5.6+ 引入 ICP:儲存引擎層在讀取索引時,直接評估 WHERE 中可用的索引列條件,過濾後再回表,減少回表次數。
EXPLAIN 顯示 Using index condition。
ORDER BY / GROUP BY 遵循最左前綴,且列順序、方向(ASC/DESC)需一致。INDEX (a,b) 支援 ORDER BY a,b、ORDER BY a DESC, b DESC,**不支援** ORDER BY a ASC, b DESC(MySQL 8.0 支援降序索引)。WHERE 固定前綴列,ORDER BY 可用後續列:WHERE a=1 ORDER BY b。-- 計算前 N 字元選擇性 SELECT COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) AS sel_10, COUNT(DISTINCT LEFT(email, 15)) / COUNT(*) AS sel_15, COUNT(DISTINCT LEFT(email, 20)) / COUNT(*) AS sel_20 FROM users; -- 選擇性接近 1.0 時,該長度足夠代表全列 ALTER TABLE users ADD INDEX idx_email_prefix (email(15));
| 反模式 | 影響 | 修正 |
|---|---|---|
| 過度索引(單表 >5 個) | 寫入放大、優化器選擇困難、空間浪費 | 定期 pt-index-usage 分析,刪除未用索引 |
隱式轉換 WHERE varchar_col = 123 | 索引失效、全表掃描 | 應用層傳字串、或加 CAST |
大字段建索引 INDEX (long_text) | 索引極大、維護成本高 | 改用前綴索引或全文索引 |
| 外鍵索引缺失 | 子表刪除/更新需鎖定父表全表掃描 | 明確建立外鍵列索引 |
ALGORITHM=INPLACE, LOCK=NONE 線上加索引,不鎖表。OPTIMIZE TABLE 或 ANALYZE TABLE 更新統計資訊、整理碎片。Innodb_index_corrupt_count、Innodb_buffer_pool_pages_free 預警。結語: 索引優化的核心是**理解 B+樹存取路徑**,遵循「最左前綴、覆蓋索引、避免回表、拒絕隱式轉換」。配合 EXPLAIN、key_len、pt-index-usage 量化分析,將索引從「玄學」變成可測量、可迭代的工程實踐。