複合索引的欄位 = Index Cond 的欄位 + Filter 裡的等值欄位
診斷的核心邏輯很單純:看它在
Filter浪費了什麼,就把那些欄位補進索引。
三步 SOP
[步驟 1] 找耗時大戶
計算 (actual time 的第二個數字) × loops,找出哪一行吃掉 80% 以上的時間
[步驟 2] 看該行有沒有大量丟棄
檢查 Rows Removed by Filter,數量達數千到數十萬就是它
[步驟 3] 組合索引欄位
索引欄位 = [Index Cond 的欄位] + [Filter 裡的等值比對 (=) 欄位]
Index Cond 和 Filter 的分界見 Index Cond 是在索引上定位,Filter 是撈出來再逐筆比;步驟 1 的乘法見 loops 大於 1 時,actual time 和 rows 是每次迴圈的平均。
案例一:索引只涵蓋了第一個條件
-> Bitmap Heap Scan on pg060 s
Filter: ((a.div_no = (div_no)::bpchar) AND (a.emp_org_id = (emp_org_id)::bpchar))
Rows Removed by Filter: 38118
-> Bitmap Index Scan on pg060_idx1
Index Cond: (vrfy_wkym = a.vrfy_wkym)Index Cond 的欄位 → vrfy_wkym
Filter 的等值欄位 → div_no, emp_org_id
CREATE INDEX idx_pg060_lookup ON pg060 (vrfy_wkym, div_no, emp_org_id);B-Tree 就能一口氣定位到 0 筆,根本不用去掃那 3.8 萬筆。
案例二:用了主鍵,但主鍵的後續欄位對不上
-> Index Scan using pg059_pk on pg059 b
Index Cond: (vrfy_wkym = a.vrfy_wkym)
Filter: ((a.div_no = (div_no)::bpchar) AND (a.emp_org_id = (unit_org_id)::bpchar))
Rows Removed by Filter: 33968「用了主鍵」不代表沒事。 這裡 Index Cond 只有 vrfy_wkym——代表主鍵的第一個欄位剛好是它,但後續欄位命不中這條 SQL 的查詢條件(主鍵可能是 (vrfy_wkym, serial_no) 之類)。
CREATE INDEX idx_pg059_lookup ON pg059 (vrfy_wkym, div_no, unit_org_id);判準不是「這張表有沒有索引」,是「
Index Cond裡出現了幾個你的查詢條件」。 只出現一個,剩下的都在Filter裡。
成效
兩張表從「掃 3 萬多筆再過濾」變成「精準命中 0~1 筆」,原本 14 秒縮到幾毫秒。
但要先確認兩件事
- 述詞真的有選擇性嗎——低基數欄位上的索引資料庫根本不會用,見 索引是拿寫入換查詢,寫多讀少的表加索引是純虧
- 統計資訊是新的嗎——猜測壞掉的時候這個判斷做不了,見 估計和實際差很多,代表資料庫對這張表的認識壞了
只有**等值比對(=)**的欄位適合往複合索引裡疊;範圍條件擺在最後一個位置之後,再往後的欄位就用不到了。