索引是關係型資料庫性能的核心杠桿。本文結合 MySQL/PostgreSQL 實戰,系統講解索引創建原則、多列索引策略、EXPLAIN 執行計劃分析、排序優化,以及避坑指南。
無索引時,查詢需全表掃描,時間複雜度 O(N);B+ 樹索引將查找降為 O(log N)。但索引並非免費:
CREATE TABLE mytable (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
category_id INT NOT NULL DEFAULT 0,
user_id INT NOT NULL DEFAULT 0,
adddate INT NOT NULL DEFAULT 0
);
-- 單列索引命名規築:表名_列名
CREATE INDEX idx_mytable_category_id ON mytable (category_id);
EXPLAIN SELECT * FROM mytable WHERE category_id = 1;
-- 關鍵輸出:
-- type: ref (非 ALL 表示用到索引)
-- key: idx_mytable_category_id
-- rows: 1 (預估掃描行數大幅下降)
-- 命名:表名_列1_列2
CREATE INDEX idx_mytable_cat_user ON mytable (category_id, user_id);
索引 (a, b, c) 可服務:
WHERE a = 1WHERE a = 1 AND b = 2WHERE a = 1 AND b = 2 AND c = 3WHERE a = 1 ORDER BY b不可服務:WHERE b = 2、WHERE a = 1 AND c = 3(跳過 b)、WHERE a > 1 AND b = 2(範圍列後列失效)。
查詢列全部包含在索引中,無需回表:
CREATE INDEX idx_cat_user_add ON mytable (category_id, user_id, adddate);
-- 查詢
SELECT category_id, user_id, adddate FROM mytable WHERE category_id = 1 AND user_id = 2;
-- Extra: Using index (覆蓋索引)
索引列順序與 ORDER BY 順序一致、方向一致時可直接利用索引有序性:
-- 索引 (category_id, user_id, adddate DESC)
SELECT * FROM mytable
WHERE category_id = 1 AND user_id = 2
ORDER BY adddate DESC;
-- Extra 無 Using filesort
技巧:若 ORDER BY 方向混合(ASC/DESC),MySQL 8.0+ 支援定義索引時指定列排序方向;舊版本需通過應用層統一方向或接受 Filesort。
| 場景 | 原因 | 對策 |
|---|---|---|
| 對索引列使用函數/運算 | 破壞列值原貌 | 移至應用層計算,或建函數索引(PG/8.0+) |
| 隱式類型轉換 | 如 VARCHAR 列比較數字 | 統一參數類型,用預處理語句 |
| LIKE 以 % 開頭 | 無法利用 B+ 樹有序性 | 改用全文索引或倒序存儲 |
| OR 條件未全覆蓋索引 | 優化器放棄索引 | 拆分為 UNION ALL |
| IS NULL / IS NOT NULL | 部分引擎不索引 NULL | 用預設值替代 NULL |
SELECT * FROM sys.schema_unused_indexes;(MySQL 5.7+ sys schema)定期清理未用索引。INDEX idx_name (col(10)),平衡區分度與空間。EXPLAIN 驗證每次變更,關注 type、key、rows、Extra。