資料庫索引解析:給網頁開發者的指南
你的網頁應用程式在開發階段感覺很順暢,但隨著資料成長,原本毫秒內回傳的查詢現在卻要花上好幾秒。使用者開始抱怨,資料庫 CPU 也飆高。罪魁禍首通常是缺少或誤用索引。索引是後端開發者最具槓桿效應的技能之一,卻經常被誤解。本指南將說明資料庫索引的運作原理、何時使用,以及如何避免常見陷阱。
什麼是資料庫索引?
把索引想像成教科書後面的索引。你不用翻遍每一頁來找某個主題,而是查閱索引,它會指引你到正確的頁面。資料庫索引的運作方式類似:它是一種資料結構,讓資料庫引擎能快速找到資料列,而不必掃描整張表。
如果沒有索引,像 SELECT * FROM users WHERE email = 'alice@example.com' 這樣的查詢會強制進行全表掃描——資料庫會讀取每一列,直到找到符合的資料。若在 email 上建立索引,資料庫就能直接跳到符合的資料列。
索引的底層運作原理
大多數關聯式資料庫預設使用 B-tree(平衡樹)索引。B-tree 會保持資料排序,並以對數時間支援搜尋、循序存取、插入和刪除。這就是為什麼即使有數百萬列,索引查找依然快速。
其他索引類型包括:
- 雜湊索引:適合精確匹配查找,不適合範圍查詢。
- 位元圖索引:對低基數資料行(例如狀態旗標)效率很高,常用於資料倉儲。
- 全文索引:專門用於搜尋文字內容。
- GiST/GIN 索引:在 PostgreSQL 中用於幾何和 JSON 資料。
對大多數網頁應用程式來說,B-tree 索引是主力。
何時建立索引
索引並非免費——它們會佔用儲存空間並拖慢寫入。請策略性地建立索引:
- WHERE 子句中的資料行:如果你經常依某個資料行篩選,就為它建立索引。
- JOIN 條件中的資料行:為外鍵建立索引以加速聯結。
- ORDER BY 中的資料行:索引可以省去排序操作。
- GROUP BY 中的資料行:索引有助於聚合查詢。
然而,請避免為很少查詢或基數極低的資料行(例如布林旗標)建立索引,除非它們與其他資料行搭配使用。
索引類型及其使用案例
| 索引類型 | 最適合 | 範例 |
|---|---|---|
| 單一資料行 | 簡單篩選 | CREATE INDEX idx_email ON users(email); |
| 複合 | 依多個資料行篩選的查詢 | CREATE INDEX idx_name_age ON users(last_name, first_name); |
| 唯一 | 強制唯一性 | CREATE UNIQUE INDEX idx_username ON users(username); |
| 部分 | 為資料列子集建立索引 | CREATE INDEX idx_active ON users(email) WHERE active = true; |
| 覆蓋 | 只需索引資料行的查詢 | CREATE INDEX idx_covering ON users(email, name); |
如何建立與驗證索引
建立索引很簡單。例如,在 PostgreSQL 中:
CREATE INDEX idx_users_email ON users(email);
建立索引後,請驗證它是否被使用。使用 EXPLAIN(或 EXPLAIN ANALYZE)來查看查詢計畫:
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'alice@example.com';
尋找「Index Scan」或「Index Only Scan」,而不是「Seq Scan」。如果你看到循序掃描,可能是因為型別不符、對資料行使用函式,或統計資訊過舊,導致索引未被使用。
常見的索引錯誤
- 為所有東西建立索引:太多索引會拖慢寫入並浪費空間。
- 忽略複合索引的順序:對於
(a, b)上的複合索引,只依b篩選的查詢無法有效使用該索引。 - 對已建立索引的資料行使用函式:
WHERE YEAR(created_at) = 2025會阻止索引使用。請改用範圍條件:WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'。 - 未更新統計資訊:資料庫依賴統計資訊來選擇索引。請定期執行
ANALYZE。 - 忽略寫入開銷:每個 INSERT、UPDATE 和 DELETE 都必須更新索引。對於寫入密集的表,請謹慎選擇。
進階技巧
覆蓋索引
覆蓋索引包含查詢所需的所有資料行,因此資料庫可以直接從索引擷取資料,而不必存取資料表。這可以大幅加速讀取密集的查詢。
部分索引
如果你經常查詢某個資料列子集(例如活躍使用者),部分索引會比完整索引更小、更快。
僅索引掃描
有些資料庫支援僅索引掃描,所有需要的資料都在索引中。這是最快的索引存取類型。
監控與維護索引
索引可能因更新和刪除而隨時間膨脹。在 PostgreSQL 中,VACUUM 和 REINDEX 有助於維持效能。在 MySQL 中,OPTIMIZE TABLE 可以重建索引。請定期檢閱慢查詢日誌,以找出缺少的索引。
常見問題
如何知道我的查詢是否使用索引?
在查詢前使用 EXPLAIN 指令(或 EXPLAIN ANALYZE)。輸出會顯示資料庫使用索引掃描還是循序掃描。
索引會太多嗎?
會。每個索引都會增加寫入操作的開銷並佔用儲存空間。請根據讀寫比例取得平衡。
叢集索引與非叢集索引有何不同?
叢集索引決定資料表中資料列的實際順序(例如 InnoDB 的主鍵)。非叢集索引是另一個指向資料列的結構。一張表只能有一個叢集索引,但可以有多個非叢集索引。
準備好最佳化你的資料庫了嗎?從分析慢查詢並在需要的地方加入索引開始。若需快速格式化與驗證 JSON,試試我們的 JSON Formatter——免費且完全在瀏覽器中執行。