「明明建了索引卻不走」是 Oracle 效能調優最常見疑問。本文深入剖析 SQL 執行九步驟、RBO/CBO 兩代優化器差異、CBO Cost 計算依賴統計資訊、十大導致不走索引的根因、系統性排查步驟、Hint 強制與追蹤調試工具,構建完整索引存取決策知識體系。
第 8 步是索引決策關鍵,前 7 步決定了「有哪些索引可用、連接順序如何」。
| 特性 | 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。
全表掃描 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 | 有統計卻走 RBO | ALTER SESSION SET OPTIMIZER_MODE=ALL_ROWS 或 Hint /*+ ALL_ROWS */ |
| 2 | WHERE 條件無索引欄位/組合索引非前導欄 | 全表掃描 | 建立正確索引、調整欄位順序 |
| 3 | 連接方式非 Nested Loop | Hash/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 | 索引欄位允許 NULL | COUNT(*)、WHERE col IS NULL 不走 B-tree | NOT NULL 約束、或函數索引 CASE WHEN col IS NULL THEN 1 END |
OPTIMIZER_MODE 非 RULE。LAST_ANALYZED) 與直方圖。-- 指定索引
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 錯誤不報錯只被忽略。
SET AUTOTRACE ON EXPLAIN STATISTICS
SELECT * FROM emp WHERE deptno = 10;
-- 輸出:執行計畫、Cost、邏輯讀/物理讀、Bytes、Rows
-- 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、物理/邏輯讀、執行次數,快速定位效能殺手。
Cost、Cardinality、Access Path 三大指標。SQL Plan Baseline (11g+) 固定核心業務計畫,防止統計波動導致計畫翻車。索引不走從來不是「資料庫壞了」,而是統計、SQL 寫法、索引設計、優化器模式某環節不匹配。系統性排查 + 科學統計管理 + 策略性 Baseline,讓索引存取決策回歸可控、可預測。