首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Oracle 為何不走索引?12 大核心原因與排查實戰指南

     編輯:Oracle 資料庫相關知識

前言:索引明明建了,為何執行計劃卻選全表掃描?

這是每位 DBA 與開發者必遇的經典靈異事件。Oracle 優化器(CBO/RBO)依據統計資訊、系統參數、SQL 寫法、連接方式等綜合因素計算 Cost,選擇最低成本路徑。索引未被使用,絕非優化器「智障」,而是某個環節不符合索引存取條件。本文梳理 12 大核心原因,並提供標準化排查清單,助你快速定位根因。

SQL 執行九步曲:優化器決策全景圖

  1. 語法分析
  2. 語義分析
  3. 視圖轉換
  4. 表達式轉換
  5. 選擇優化器(RBO vs CBO)
  6. 選擇連接方式(NL / SMJ / HJ)
  7. 選擇連接順序(驅動表誰來當)
  8. 選擇資料存取路徑(全表掃描 vs 索引掃描 vs 其他)
  9. 執行計劃生效

第 5-8 步是索引是否被採用的關鍵決策點。

兩大優化器:RBO 退役,CBO 稱王

  • RBO (Rule-Based Optimizer):Oracle 6 時代產物,依固定規則排名(如唯一索引 > 非唯一索引 > 全表掃描),不敏感資料分布,已停止發展,嚴禁生產使用。
  • CBO (Cost-Based Optimizer):Oracle 7+ 引入,核心邏輯:Cost = I/O Cost + CPU Cost。依賴 統計資訊(表/索引/欄位/直方圖)估算基數與選擇性。CBO 選擇的計劃通常不差於 RBO,且對開發者 SQL 撰寫要求較低。

檢查優化器模式:SHOW PARAMETER OPTIMIZER_MODE。預設 CHOOSE(有統計用 CBO,無統計用 RBO)。生產環境強制 ALL_ROWS 或 FIRST_ROWS_n,禁用 RULE。

12 大索引失效根因與排查順序

1. 優化器模式錯誤:被迫使用 RBO

OPTIMIZER_MODE = RULE 或 Session 級 ALTER SESSION SET OPTIMIZER_MODE = RULE。RBO 不看統計資訊,若 SQL 寫法不符規則(如連接順序、WHERE 條件順序),索引直接失效。修正:實例/Session 級設定 ALL_ROWS。

2. WHERE 條件未包含索引前導欄

複合索引 (A, B, C),查詢 WHERE B = 1 AND C = 2 缺少前導欄 A,無法做索引範圍掃描,僅能 INDEX FULL SCAN 或 INDEX FAST FULL SCAN,CBO 可能判定不如全表掃描。修正:調整索引欄順序、或新增單欄索引、或改寫 SQL 補上前導欄(即使是常數)。

3. 連接方式不支援索引利用

連接方式內表索引利用說明
Nested Loops (NL)✅ 完美支援內表逐行探查,索引範圍/唯一掃描極高效
Sort Merge Join (SMJ)⚠ 僅免排序索引有序可省去 SORT JOIN 步驟,但無法加速合併階段
Hash Join (HJ)❌ 幾乎無效建構雜湊表需全掃描內表,索引存在與否無影響

對策:若內表有高選擇性索引,強制 NL Join(USE_NL);或收集統計資讓 CBO 自然選 NL。

4. 連接順序導致外表無法用索引

NL Join 中,外表(驅動表)存取路徑固定為全表掃描或索引全掃描,無法利用連接鍵索引做範圍掃描。若 EMP.DEPTNO 有索引但 EMP 被選為外表,該索引廢用。修正:調整連接順序(ORDERED、LEADING Hint),讓有索引的表成為內表。

5. 系統字典表/視圖未分析,誤導優化器

查詢 DBA_*、V$* 等字典視圖,底層基表無統計資訊,CBO 基於預設值估算,產生極差計劃。禁忌:絕不可自行 ANALYZE 字典表,會導致死鎖或系統效能崩潰。對策:使用 DBMS_STATS.GATHER_FIXED_OBJECTS_STATS 官方收集固定物件統計。

6. 索引欄參與函數運算

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))。
  • 改寫 SQL:WHERE NAME = 'ABC'、WHERE CREATE_DATE >= TRUNC(SYSDATE) AND CREATE_DATE < TRUNC(SYSDATE+1)。

7. 隱性資料型別轉換觸發函數

WHERE CHAR_COL = 123 → Oracle 隱性 TO_NUMBER(CHAR_COL) = 123,等同第 6 點。修正:明確轉換常數 WHERE CHAR_COL = '123',或修正欄定義為 NUMBER。

8. 統計資訊缺失或過期

CBO 無統計資訊時退回動態採樣或預設選擇性(如等值 1%,範圍 5%),嚴重偏離實際。修正:

  • 定期 DBMS_STATS.GATHER_TABLE_STATS(..., METHOD_OPT => 'FOR ALL COLUMNS SIZE AUTO')。
  • 高變動表建立排程,每日/每小時收集。
  • 關鍵欄位建立直方圖(SIZE SKEWONLY 或指定桶數)。

9. 索引選擇性過低

欄位唯一值極少(如性別 M/F、狀態 0/1/2),CBO 判定掃描索引 + 回表成本 > 全表掃描。案例:100 萬行,DEPTNO 僅 4 個值,每值 25 萬行。查 DEPTNO=10 走索引需 25 萬次隨機 I/O,遠慢於順序全掃。對策:

  • 不建立此類低選擇性索引(除非用於約束)。
  • 若資料傾斜嚴重(99 萬行=10,其餘極少),建立直方圖讓 CBO 識別「熱門值走全表、冷門值走索引」。

10. 索引欄允許 NULL,導致 COUNT(*)、IS NULL 無法用索引

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 約束,索引自動包含所有行。

11. 並行查詢 (PQO) 強制全表掃描

PARALLEL Hint 或表屬性 DEGREE > 1 觸發 PQO,PQO 傾向全表掃描分發給多進程,索引範圍掃描難以並行化。對策:OLTP 核心表關閉並行屬性;報表查詢接受 PQO 全掃描。

12. 綁定變量窺探與適應性游標共享副作用

綁定變量 :b1 在硬解析時「窺探」首次值生成計劃,後續不同值複用同一計劃可能極差。現象:首次執行 :b1 = 'RARE_VALUE' 走索引,後續 :b1 = 'COMMON_VALUE' 仍走索引導致災難。修正:

  • Oracle 11g+ 適應性游標共享 (ACS) 自動生成多個計劃,確保 _OPTIMIZER_ADAPTIVE_CURSOR_SHARING = TRUE。
  • 關鍵 SQL 使用字面值(Literal)而非綁定變量,或 OPT_ESTIMATE Hint 修正基數。

標準化排查清單(建議收藏)

  1. 確認 OPTIMIZER_MODE 非 RULE。
  2. 檢查 WHERE 條件是否包含索引前導欄。
  3. 查看執行計劃連接方式(NL/SMJ/HJ)與連接順序。
  4. 排除字典表/視圖干擾。
  5. 掃描 SQL 是否對索引欄用函數/運算/隱性轉換。
  6. 驗證表/索引統計資訊新鮮度(LAST_ANALYZED)、直方圖是否存在。
  7. 評估索引選擇性(DISTINCT_KEYS / NUM_ROWS)。
  8. 確認索引欄 NULL 屬性與查詢條件匹配。
  9. 檢查並行屬性/Hint 是否強制 PQO。
  10. 分析綁定變量值分布與 ACS 行為。

總結

索引未被使用,90% 以上源於統計資訊不準、SQL 寫法破壞索引鍵值、連接方式/順序不當。建立「統計資訊維護 SOP、SQL 審核規範、執行計劃基準線」三位一體治理體系,才能從根源消除「索引失效」疑難雜症。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved