核心觀念:Index Scan 不一定比 Full Table Scan 快
當 Oracle 在有索引的情況下選擇 Full Table Scan (FTS),通常不是優化器 Bug,而是基於成本估算 (Cost-Based Optimizer) 的理性決策。常見兩大原因:
- 統計資料缺失或過舊,導致 Cardinality 估算偏差。
- 依據實際資料分布與存取模式,FTS 確實比 Index Range Scan + Table Access by RowID 更低成本。
關鍵指標:Clustering Factor (CF) 與 Filtering Factor (FF)
Clustering Factor (CF) — 索引與表資料的排序一致性
- 定義:掃描索引葉子塊時,對應需要讀取的**資料塊數量**期望值。
- 理想情況:索引順序與表資料物理順序一致,CF ≈ 表區塊數 (Blocks)。
- 惡化情況:大量隨機 INSERT/UPDATE 導致 RowID 跳躍,CF 可遠大於表區塊數,甚至接近行數 (Rows)。
- 維護:
ALTER INDEX idx_name REBUILD 或 REBUILD ONLINE 可重設 CF 接近理想值。
Filtering Factor (FF) — 查詢選擇率
- 定義:
WHERE 條件過濾後的結果集佔全表行數比例。
- 由優化器根據統計資料 (Histogram、Min/Max、NDV) 估算。
- FF 越小 (高選擇性),索引優勢越大;FF 越大 (低選擇性),FTS 優勢越大。
成本估算簡化模型
Index Access Cost ≈ FF × (CF + Index Blocks)
Full Table Scan Cost ≈ Table Blocks
當 FF × (CF + Index Blocks) > Table Blocks 時,優化器傾向 FTS。
實戰案例驗證 (Oracle 8.1.7 on HP)
測試表 MYTABLES:32.5 萬行,主鍵 MYID (1 ~ 409,654),CF > Table Blocks (因歷史大量 DML 導致)。
查詢 1:低選擇性 (FF ≈ 100%)
SELECT * FROM mytables WHERE myid >= 1;
- 預估返回 31.8 萬行 (Card=318474)
- 執行計畫:TABLE ACCESS FULL,Cost=3132,Physical Reads=19,828
查詢 2:高選擇性 (FF ≈ 1%)
SELECT * FROM mytables WHERE myid >= 400000;
- 預估返回 663 行 (Card=663)
- 執行計畫:INDEX RANGE SCAN (PK_MYTABLES) → TABLE ACCESS BY INDEX ROWID,Cost=346,Physical Reads=292
實務啟示
- 定期收集統計資料:
DBMS_STATS.GATHER_TABLE_STATS,含 METHOD_OPT=>'FOR ALL COLUMNS SIZE AUTO' 建立直方圖。
- 監控 CF 變化:
SELECT index_name, clustering_factor FROM user_indexes; 當 CF 明顯大於 Table Blocks 時排程 Rebuild。
- 撰寫 SQL 前估算 FF:粗略判斷回傳比例,若 > 5-10% 且無覆蓋索引,FTS 多半更優。
- 覆蓋索引 (Covering Index):將查詢欄位全部包含在索引中,可避免 Table Access by RowID,大幅降低 CF 影響。
下一篇將探討「函數索引與虛擬欄位」及「索引失效常見陷阱」。