覆蓋索引與多欄索引 (Covering & Composite Indexes)

★★★★Reviewed

FromSQL Server 2025 Query Performance TuningDDIA

📚 From the Books

覆蓋索引與多欄索引 (Covering & Composite Indexes)Indexing

兩個把 secondary index 榨到極致的手法。Covering index:查詢需要的欄位全在索引裡,直接讀索引就回答、免回表。Composite index:多欄組成一把有序的複合鍵,但只能從最左前綴用起——欄位順序就是它的命。

🧠 Intuition

TIP

把 composite index 想成電話簿按「姓, 名」排序:你能用「姓」或「姓+名」快速找人,但**不能單用「名」**找——因為簿子不是按名排的。covering index 則是「連電話號碼都印在索引頁上」,查到就不必翻回內文。

⚖️ Tradeoffs

Covering index — 免回表,但讓索引變胖
  • 收益:index-only scan 把「索引存取 + 回表隨機 I/O」砍成「只讀索引」,對高頻查詢是數量級的提升。
  • 代價:塞進去的欄位讓索引葉節點變大、佔更多快取、寫入時要多維護一份。
  • 折衷:SQL Server INCLUDE / PG INCLUDE 把欄位放葉節點但不進鍵——既覆蓋查詢又不撐大 B-Tree 內部節點、不影響鍵排序。
欄位順序決定可用性 — composite 的成敗關鍵
  • (a, b) 能服務 WHERE a=?WHERE a=? AND b=?、以及 ORDER BY a, b
  • WHERE b=? 用不到它——等於白建。
  • 經驗法則:等值條件(equality)欄位放前面,範圍條件(range)放後面。一旦遇到 range 欄位,其後欄位就無法再用索引排序(range 之後失序)。
選擇性與排序 — 兩種排欄位的思路會打架
  • 「高選擇性欄位放前面」能更快縮小掃描範圍(傳統建議)。
  • 但若查詢有固定的 equality 前綴 + 之後 ORDER BY,欄位順序得遷就查詢模式而非單純選擇性,才能同時吃到 WHERE 過濾與免排序。
  • 結論:composite index 是為具體查詢量身打造的,不存在「通用最佳順序」。
索引選擇 vs 寫入成本 — 別把每個查詢都餵一條索引
  • 每多一條 secondary index,INSERT/UPDATE/DELETE 都要多維護一棵 B-Tree(寫放大、頁分裂、WAL 變多)。
  • 過度索引會拖垮寫入、膨脹儲存,optimizer 也要在更多候選間挑選。
  • 策略:用少數設計良好的 composite/covering index 覆蓋多種查詢(靠最左前綴一索引多用),勝過為每個查詢各建一條窄索引。

🔑 Takeaways

✍️ My Notes

2026-07-01

  • Q: 我有 (a, b) 索引,查詢是 WHERE a = 1 AND b > 10 ORDER BY c,這索引幫得上多少?
  • A: a = 1 吃到 equality 前綴,b > 10 是 range、能用索引繼續過濾——到這裡很好。但 ORDER BY c 吃不到:因為 b 是 range 條件,其後的順序在索引中已被打散,c 又不在索引內,所以 optimizer 仍得做一次 sort。若 c 排序很關鍵,得重新設計成 (a, c) 之類、犧牲 b 的索引過濾換取免排序——這正是「欄位順序服務具體查詢」的典型抉擇。

📖 Further Reading