索引最佳化 (SQL Index Optimization)
索引 (Index) 可以大幅提升查詢效能,但建立過多或不當的索引反而會降低效能。這篇文章介紹索引使用的最佳實踐。
什麼時候該建立索引?
- 經常出現在 WHERE 條件的欄位
- 經常用於 JOIN 連接的欄位
- 經常用於 ORDER BY 排序的欄位
- 經常用於 GROUP BY 分組的欄位
- 高選擇性的欄位(不同值很多,如 email、身分證字號)
什麼時候不該建立索引?
- 資料量很小的表(全表掃描可能更快)
- 經常大量更新的欄位(每次更新都要維護索引)
- 低選擇性的欄位(如性別、布林值,只有少數幾種值)
- 很少用於查詢條件的欄位
索引使用的最佳實踐
1. 使用複合索引時注意欄位順序
複合索引遵循「最左前綴原則」,只有從最左邊的欄位開始連續使用才會生效:
-- 建立複合索引
CREATE INDEX idx_name_city ON customers(name, city, age);
| 查詢條件 | 索引是否生效 |
|---|---|
| WHERE name = 'xxx' | ✓ |
| WHERE name = 'xxx' AND city = 'yyy' | ✓ |
| WHERE name = 'xxx' AND city = 'yyy' AND age = 30 | ✓ |
| WHERE city = 'yyy' | ✗(跳過了 name) |
| WHERE name = 'xxx' AND age = 30 | 部分(只用到 name) |
2. 避免在索引欄位上使用函數
-- ✗ 不好:索引失效
SELECT * FROM orders WHERE YEAR(order_date) = 2024;
-- ✓ 好:索引可以使用
SELECT * FROM orders
WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01';
3. 避免在索引欄位上做運算
-- ✗ 不好:索引失效
SELECT * FROM products WHERE price * 1.1 > 1000;
-- ✓ 好:索引可以使用
SELECT * FROM products WHERE price > 1000 / 1.1;
4. 注意 LIKE 的使用方式
-- ✓ 索引可以使用(前綴匹配)
SELECT * FROM customers WHERE name LIKE '張%';
-- ✗ 索引失效(萬用字元在前)
SELECT * FROM customers WHERE name LIKE '%張';
SELECT * FROM customers WHERE name LIKE '%張%';
5. 使用覆蓋索引 (Covering Index)
如果索引包含了查詢所需的所有欄位,資料庫就不需要再去讀取資料表,這稱為覆蓋索引:
-- 建立索引
CREATE INDEX idx_covering ON orders(customer_id, order_date, total);
-- 這個查詢只需要讀取索引,不需要讀取資料表
SELECT customer_id, order_date, total FROM orders WHERE customer_id = 1;
6. 定期維護索引
索引隨著資料的新增、刪除會產生碎片化,定期重建可以維持效能:
-- SQL Server
ALTER INDEX idx_name ON table_name REBUILD;
-- MySQL
ALTER TABLE table_name ENGINE=InnoDB; -- 重建整個表
OPTIMIZE TABLE table_name;
-- PostgreSQL
REINDEX INDEX idx_name;
查看索引使用情況
查看執行計劃
-- MySQL
EXPLAIN SELECT * FROM customers WHERE name = '張一';
-- SQL Server
SET SHOWPLAN_TEXT ON;
SELECT * FROM customers WHERE name = '張一';
-- PostgreSQL
EXPLAIN ANALYZE SELECT * FROM customers WHERE name = '張一';
執行計劃會顯示查詢是否使用了索引,以及使用了哪個索引。
索引類型選擇
| 索引類型 | 適用情境 |
|---|---|
| B-Tree(預設) | 等值查詢、範圍查詢、排序 |
| Hash | 等值查詢(不支援範圍) |
| Full-Text | 全文搜尋 |
| Spatial | 地理空間資料 |
索引設計原則總結
- 只為經常查詢的欄位建立索引
- 優先考慮複合索引,減少索引數量
- 將選擇性高的欄位放在複合索引前面
- 避免過度索引,每個索引都會增加寫入成本
- 定期檢視和調整索引策略