建索引的代價是毫秒,收益可能是秒
建索引是拿寫入成本與儲存空間換讀取速度(見 索引是拿寫入換查詢,寫多讀少的表加索引是純虧)。那篇講的是這筆交易的形狀;這篇講怎麼判斷這筆交易划不划算。 四個維度。
1. 讀寫比例
Web / API 系統大多是讀多寫少,常見 9:1 或 8:2。如果一張表 90% 是 SELECT、10% 是 INSERT/UPDATE,替查詢省下的時間遠遠壓倒寫入多出來的那幾毫秒。
例外:高頻寫入的 log 表、IoT 秒級上報表(寫入佔 95% 以上),建索引要極度謹慎。
2. 兩邊根本不在同一個數量級
| 付出(寫入變慢) | INSERT 多維護一棵 B-Tree 約 0.1 ~ 1 ms |
UPDATE 只有改到索引包含的欄位時才要更新索引;只改無關欄位時 PostgreSQL 的 **HOT(Heap-Only Tuple)**機制可能連索引都不用碰(這句把 HOT 當成常態,有爭議 → PG 的 HOT 什麼時候會失效?) | |
| 獲得(讀取變快) | 查詢從 14,329 ms 降到 2 ms,單次省下 14 秒的 CPU 與磁碟 I/O |
拿寫入增加 1 ms 去換單次讀取省下 14,000 ms——就算這張表讀寫比是 1:1,也是壓倒性划算的交易。
3. 慢查詢會霸佔連線,這才是非建不可的理由
這一點決定「該不該優先處理」,而不只是「划不划算」:
一條 SQL 跑 14 秒
→ 它佔住一個連線整整 14 秒,而且期間 CPU 全力運轉
→ 同時有 10 個人或報表在查 → 吃掉 10 個連線
→ 後面進來的請求開始排隊
→ 連線池耗盡(Connection Pool Exhaustion)
→ 整個系統癱瘓
PostgreSQL 的連線數是有限的(例如上限 100~200)。所以:
任何單次執行超過數秒的高頻或常態查詢,通常都必須優先解決,否則會拖垮整座資料庫的寫入與讀取。
一條慢查詢的傷害不會停在它自己身上。
4. 決策檢查清單
| 評估問題 | 傾向「建」 | 傾向「不建/另尋他法」 |
|---|---|---|
| 讀取耗時多長? | 數百毫秒到數秒 | 原本就只要 5~10 ms |
| 寫入頻率? | 正常業務寫入、批次寫入、讀多寫少 | 每秒數萬筆的極高頻純寫入 |
| 索引欄位更新頻率? | 識別碼、年月、狀態這類很少被 UPDATE 的欄位 | 數值每秒都在異動 |
| 這張表已有幾個索引? | 只有主鍵或 1~2 個(維護成本低) | 已有 15 個以上(先清理廢索引) |
和另外兩條判準的關係
這四個維度回答的是「值不值得」。還有兩個前置問題要先過:
- 這個索引資料庫會不會用?——低選擇性的欄位它根本不會走,見 索引是拿寫入換查詢,寫多讀少的表加索引是純虧
- 現在判斷得準嗎?——統計資訊壞掉時這個判斷做不了,見 估計和實際差很多,代表資料庫對這張表的認識壞了
確認要建之後,欄位怎麼選見 複合索引的欄位 = Index Cond 的欄位 + Filter 裡的等值欄位。