這是每位 DBA 與開發者必遇的經典靈異事件。Oracle 優化器(CBO/RBO)依據統計資訊、系統參數、SQL 寫法、連接方式等綜合因素計算 Cost,選擇最低成本路徑。索引未被使用,絕非優化器「智障」,而是某個環節不符合索引存取條件。本文梳理 12 大核心原因,並提供標準化排查清單,助你快速定位根因。
第 5-8 步是索引是否被採用的關鍵決策點。
Cost = I/O Cost + CPU Cost。依賴 統計資訊(表/索引/欄位/直方圖)估算基數與選擇性。CBO 選擇的計劃通常不差於 RBO,且對開發者 SQL 撰寫要求較低。檢查優化器模式:SHOW PARAMETER OPTIMIZER_MODE。預設 CHOOSE(有統計用 CBO,無統計用 RBO)。生產環境強制 ALL_ROWS 或 FIRST_ROWS_n,禁用 RULE。
OPTIMIZER_MODE = RULE 或 Session 級 ALTER SESSION SET OPTIMIZER_MODE = RULE。RBO 不看統計資訊,若 SQL 寫法不符規則(如連接順序、WHERE 條件順序),索引直接失效。修正:實例/Session 級設定 ALL_ROWS。
複合索引 (A, B, C),查詢 WHERE B = 1 AND C = 2 缺少前導欄 A,無法做索引範圍掃描,僅能 INDEX FULL SCAN 或 INDEX FAST FULL SCAN,CBO 可能判定不如全表掃描。修正:調整索引欄順序、或新增單欄索引、或改寫 SQL 補上前導欄(即使是常數)。
| 連接方式 | 內表索引利用 | 說明 |
|---|---|---|
| Nested Loops (NL) | ✅ 完美支援 | 內表逐行探查,索引範圍/唯一掃描極高效 |
| Sort Merge Join (SMJ) | ⚠ 僅免排序 | 索引有序可省去 SORT JOIN 步驟,但無法加速合併階段 |
| Hash Join (HJ) | ❌ 幾乎無效 | 建構雜湊表需全掃描內表,索引存在與否無影響 |
對策:若內表有高選擇性索引,強制 NL Join(USE_NL);或收集統計資讓 CBO 自然選 NL。
NL Join 中,外表(驅動表)存取路徑固定為全表掃描或索引全掃描,無法利用連接鍵索引做範圍掃描。若 EMP.DEPTNO 有索引但 EMP 被選為外表,該索引廢用。修正:調整連接順序(ORDERED、LEADING Hint),讓有索引的表成為內表。
查詢 DBA_*、V$* 等字典視圖,底層基表無統計資訊,CBO 基於預設值估算,產生極差計劃。禁忌:絕不可自行 ANALYZE 字典表,會導致死鎖或系統效能崩潰。對策:使用 DBMS_STATS.GATHER_FIXED_OBJECTS_STATS 官方收集固定物件統計。
WHERE UPPER(NAME) = 'ABC'、WHERE TRUNC(CREATE_DATE) = SYSDATE、WHERE NVL(STATUS,0) = 1。函數破壞欄原始值,B-Tree 索引無法直接匹配。修正:
CREATE INDEX IDX_UPPER_NAME ON T(UPPER(NAME))。WHERE NAME = 'ABC'、WHERE CREATE_DATE >= TRUNC(SYSDATE) AND CREATE_DATE < TRUNC(SYSDATE+1)。WHERE CHAR_COL = 123 → Oracle 隱性 TO_NUMBER(CHAR_COL) = 123,等同第 6 點。修正:明確轉換常數 WHERE CHAR_COL = '123',或修正欄定義為 NUMBER。
CBO 無統計資訊時退回動態採樣或預設選擇性(如等值 1%,範圍 5%),嚴重偏離實際。修正:
DBMS_STATS.GATHER_TABLE_STATS(..., METHOD_OPT => 'FOR ALL COLUMNS SIZE AUTO')。SIZE SKEWONLY 或指定桶數)。欄位唯一值極少(如性別 M/F、狀態 0/1/2),CBO 判定掃描索引 + 回表成本 > 全表掃描。案例:100 萬行,DEPTNO 僅 4 個值,每值 25 萬行。查 DEPTNO=10 走索引需 25 萬次隨機 I/O,遠慢於順序全掃。對策:
B-Tree 索引不儲存全 NULL 鍵值。WHERE COL IS NULL、COUNT(*)、COUNT(COL) 均無法走該索引。修正:
COUNT(*) 改用 COUNT(1) 或 COUNT(PK_COL)(PK 不可為 NULL)。CREATE INDEX IDX_COL_NVL ON T(NVL(COL, 'NULL'))。NOT NULL 約束,索引自動包含所有行。PARALLEL Hint 或表屬性 DEGREE > 1 觸發 PQO,PQO 傾向全表掃描分發給多進程,索引範圍掃描難以並行化。對策:OLTP 核心表關閉並行屬性;報表查詢接受 PQO 全掃描。
綁定變量 :b1 在硬解析時「窺探」首次值生成計劃,後續不同值複用同一計劃可能極差。現象:首次執行 :b1 = 'RARE_VALUE' 走索引,後續 :b1 = 'COMMON_VALUE' 仍走索引導致災難。修正:
_OPTIMIZER_ADAPTIVE_CURSOR_SHARING = TRUE。OPT_ESTIMATE Hint 修正基數。OPTIMIZER_MODE 非 RULE。LAST_ANALYZED)、直方圖是否存在。DISTINCT_KEYS / NUM_ROWS)。索引未被使用,90% 以上源於統計資訊不準、SQL 寫法破壞索引鍵值、連接方式/順序不當。建立「統計資訊維護 SOP、SQL 審核規範、執行計劃基準線」三位一體治理體系,才能從根源消除「索引失效」疑難雜症。