首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle 為何選擇索引掃描?CBO/RBO 優化器決策邏輯、十大不走索引原因與 Hint 強制技巧

     編輯:Oracle 資料庫基礎

「明明建了索引卻不走」是 Oracle 效能調優最常見疑問。本文深入剖析 SQL 執行九步驟、RBO/CBO 兩代優化器差異、CBO Cost 計算依賴統計資訊、十大導致不走索引的根因、系統性排查步驟、Hint 強制與追蹤調試工具,構建完整索引存取決策知識體系。

一、SQL 執行九步驟(優化器決策鏈)

  1. 語法分析
  2. 語義分析 (物件存在、權限)
  3. 視圖轉換
  4. 表達式轉換
  5. 選擇優化器 (RBO vs CBO)
  6. 選擇連接方式 (NL / Hash / Sort-Merge)
  7. 選擇連接順序 (驅動表)
  8. 選擇資料存取路徑 (全表掃描 vs 索引掃描 vs 其他)
  9. 執行計畫

第 8 步是索引決策關鍵,前 7 步決定了「有哪些索引可用、連接順序如何」。

二、RBO vs CBO 核心差異

特性RBO (Rule-Based)CBO (Cost-Based)
決策依據固定規則排名 (索引 > 全表)Cost 模型 (I/O + CPU + 網路)
對資料敏感度不敏感 (不看統計)高度敏感 (依賴統計資訊)
新特性支援無 (Hash Join、Partition、Parallel 等)全支援
開發者負擔高 (需寫符合規則的 SQL)低 (優化器自動尋優)
現狀10g 後不再增強、文件標註揮發預設、持續演進

參數 OPTIMIZER_MODE:CHOOSE (預設,有統計用 CBO,無統計用 RBO)、RULE、FIRST_ROWS_n、ALL_ROWS。

三、CBO Cost 計算核心公式 (簡化)


全表掃描 Cost ≈ (Blocks / MBRC) * MREADTIM / SREADTIM + CPU Cost
索引範圍掃描 Cost ≈ BLEVEL + ceil(Leaf_Blocks * Selectivity) + ceil(Clustering_Factor * Selectivity)
  • Selectivity = 1 / NDV (Number of Distinct Values),直方圖時更精確。
  • Clustering_Factor 接近 Blocks → 索引順序與表順序一致 (好);接近 Num_Rows → 順序亂 (差)。

四、十大不走索引根因與對策

#根因現象對策
1優化器模式為 RULE有統計卻走 RBOALTER SESSION SET OPTIMIZER_MODE=ALL_ROWS 或 Hint /*+ ALL_ROWS */
2WHERE 條件無索引欄位/組合索引非前導欄全表掃描建立正確索引、調整欄位順序
3連接方式非 Nested LoopHash/Sort-Merge Join 不用內表索引Hint /*+ USE_NL(b) */ 或調整驅動表
4連接順序導致外表先掃描外表全表掃描,索引在內表Hint /*+ LEADING(a b) */ 指定驅動表
5涉及字典表/視圖未分析極差計畫勿自行分析字典;用 Hint 繞過
6索引欄位用於函數參數UPPER(name) = 'ABC' 不走索引建立函數索引 CREATE INDEX ... ON (UPPER(name))
7隱性型別轉換char_col = 123 → TO_NUMBER(char_col)統一型別、顯式 TO_CHAR/TO_NUMBER
8統計資訊過期/缺失Cost 嚴重偏離定期 DBMS_STATS.GATHER_*、直方圖
9索引選擇性太低 (NDV 太小)如性別、狀態碼,全表掃描更快接受全表掃描、或建位圖索引 (低並發)
10索引欄位允許 NULLCOUNT(*)、WHERE col IS NULL 不走 B-treeNOT NULL 約束、或函數索引 CASE WHEN col IS NULL THEN 1 END

五、系統性排查步驟 (按順序執行)

  1. 確認 OPTIMIZER_MODE 非 RULE。
  2. 檢查 WHERE 條件是否包含索引前導欄位。
  3. 查看執行計畫連接方式 (NL/Hash/SM) 與順序 (LEADING)。
  4. 排除字典表/視圖干擾。
  5. 檢查函數/隱性轉換。
  6. 驗證統計資訊新鮮度 (LAST_ANALYZED) 與直方圖。
  7. 評估索引選擇性 (NDV/Clustering Factor)。
  8. 檢查 NULL 值影響。
  9. 排除並行查詢 (PQO) 干擾。
  10. 檢查 Bind Variable Peeking 影響。
  11. 以上皆無 → Hint 強制 或 SQL Profile / Baseline** 固定計畫。

六、Hint 強制索引語法與陷阱

-- 指定索引
SELECT /*+ INDEX(emp emp_pk) */ * FROM emp WHERE empno = 100;

-- 指定索引結合 (組合索引)
SELECT /*+ INDEX(emp emp_dept_job_ix) */ * FROM emp WHERE deptno = 10 AND job = 'CLERK';

-- 禁止索引 (強制全表)
SELECT /*+ FULL(emp) */ * FROM emp WHERE deptno = 10;

-- 多 Hint 組合
SELECT /*+ LEADING(e d) USE_NL(d) INDEX(e emp_pk) */ ...

語法規則:/*+ 緊跟 SELECT,+ 不可有空格;Hint 錯誤不報錯只被忽略。

七、兩大追蹤調試工具

1. EXPLAIN PLAN + AUTOTRACE

SET AUTOTRACE ON EXPLAIN STATISTICS
SELECT * FROM emp WHERE deptno = 10;
-- 輸出:執行計畫、Cost、邏輯讀/物理讀、Bytes、Rows

2. TKPROF 全會話追蹤

-- 1. 找 SID/SERIAL#
SELECT sid, serial#, username FROM v$session WHERE username = 'SCOTT';

-- 2. 開啟追蹤 (需 SYS/DBA)
EXEC DBMS_SYSTEM.SET_SQL_TRACE_IN_SESSION(sid, serial#, TRUE);

-- 3. 執行目標應用/腳本

-- 4. 關閉追蹤
EXEC DBMS_SYSTEM.SET_SQL_TRACE_IN_SESSION(sid, serial#, FALSE);

-- 5. 處理追蹤檔
tkprof /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_12345.trc \
       output.txt EXPLAIN=scott/tiger SYS=NO

output.txt 含每條 SQL 的計畫、CPU、Elapsed、物理/邏輯讀、執行次數,快速定位效能殺手。

八、最佳實務總結

  • 統計資訊是 CBO 生命線:自動收集 + 關鍵表手動收集 + 直方圖。
  • 索引設計遵循「高選擇性、前導欄在 WHERE、避免函數/轉換」。
  • 執行計畫讀懂 Cost、Cardinality、Access Path 三大指標。
  • Hint 僅為戰術手段,策略靠統計與索引設計。
  • 建立 SQL Plan Baseline (11g+) 固定核心業務計畫,防止統計波動導致計畫翻車。

索引不走從來不是「資料庫壞了」,而是統計、SQL 寫法、索引設計、優化器模式某環節不匹配。系統性排查 + 科學統計管理 + 策略性 Baseline,讓索引存取決策回歸可控、可預測。

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