**「解析一次,執行多次」** 是 Oracle 高效能的基石。SQL 進入資料庫後,優先在 **Shared Pool (共用池, SGA 組件)** 尋找相同的已解析游標。**硬解析** 消耗 CPU、Latch、Heap 記憶體;**軟解析** 僅做語法/語義檢查與權限驗證,開銷極低。本文深度解析 **精確匹配三鐵律、Bind Variable 關鍵作用、Cursor Sharing 參數策略、解析量監控與優化實戰**。
只有同時滿足三條件,才能複用現有 Child Cursor (軟解析):
-- 以下四條在 Library Cache 中是**四個不同 Parent Cursor**
SELECT * FROM emp; -- Hash 1
SELECT * FROM EMP; -- Hash 2 (大小寫不同)
SELECT * FROM emp ; -- Hash 3 (結尾空格)
SELECT /* hint */ * FROM emp; -- Hash 4 (註釋不同)
**對策:** 程式碼中 SQL **常數化、格式統一、使用 Bind Variable**。
-- User JACK: 私有同義詞 sal_limit → JACK.SAL_LIMIT
-- User JILL: 私有同義詞 sal_limit → JILL.SAL_LIMIT (不同物件)
-- 兩者執行相同 SQL: SELECT * FROM sal_limit
-- → 無法共享 Child Cursor,因 OBJECT_ID 不同
**對策:** 統一使用 **Public Synonym** 或 **Schema Qualified Name** (`SCHEMA.TABLE`),避免私有同義詞導致「同文本異物件」。
-- 可共享
SELECT * FROM emp WHERE empno = :b1; -- :b1
SELECT * FROM emp WHERE empno = :b1; -- :b1
-- 不可共享 (綁定變數名稱不同)
SELECT * FROM emp WHERE empno = :b1; -- :b1
SELECT * FROM emp WHERE empno = :b2; -- :b2 → 新 Child Cursor
**對策:** 程式框架層**統一綁定變數命名規範** (如 `:p1, :p2...` 或 `:empno, :ename`),禁止動態生成變數名。
| 指標 | 硬解析 | 軟解析 | |
|---|---|---|---|
| CPU 消耗 | 高 (語法/語義/優化/行源生成) | 極低 (僅查找 Hash、權限檢查) | |
| Latch 爭用 | Library Cache Latch / Shared Pool Latch 熱點 | 幾乎無 | |
| Shared Pool 記憶體 | 每個新 Cursor 佔用 1-5 MB+ (Heap 0-6) | 零增長 | |
| 執行計畫穩定性 | 每次可能不同 (統計資訊變化、Bind Peeking) | 複用既有計畫,穩定可預測 |
| 值 | 行為 | 適用場景 | 風險 |
|---|---|---|---|
| EXACT (預設) | 精確匹配三鐵律 | 正常開發、已規範 Bind Variable | 無 |
| FORCE | **強制將 Literal 替換為系統生成 Bind Variable** (如 `:SYS_B_0`) | **緊急救援**:Legacy 系統無法改碼、Literal 導致硬解析風暴 | 1. 直方圖失效 (CBO 看不見值分布) 2. 函數索引失效 3. 可能產生次優計畫 4. 11g+ Adaptive Cursor Sharing 部分緩解 |
| SIMILAR (11g 棄用) | 僅替換「安全」Literal (無直方圖欄位) | 過渡方案 | 同 FORCE,且行為難預測 |
建議:**開發階段強制 EXACT + 代碼審查 Bind Variable 覆蓋率 100%**;生產突發解析風暴時,**短期開啟 FORCE 止血,同步排程改碼上線,改碼完成後關回 EXACT**。
-- 1. 硬/軟解析比率 (系統級)
SELECT
(1 - (session cursor cache hits) / (parse count (total))) * 100 AS hard_parse_pct
FROM v$sysstat
WHERE name IN ('parse count (total)', 'session cursor cache hits');
-- 2. 高解析 SQL Top 10
SELECT sql_id, sql_text, parse_calls, executions,
round(parse_calls/executions, 2) AS parse_per_exec
FROM v$sqlarea
WHERE executions > 100
ORDER BY parse_calls DESC
FETCH FIRST 10 ROWS ONLY;
-- 3. Child Cursor 爆炸 (同一 SQL_ID 多 Child)
SELECT sql_id, count(*) AS child_count
FROM v$sql
GROUP BY sql_id
HAVING count(*) > 20
ORDER BY child_count DESC;
-- 4. Shared Pool 記憶體壓力
SELECT * FROM v$sgastat WHERE pool = 'shared pool' AND name LIKE '%free%';
SESSION_CACHED_CURSORS (預設 50, 建議 200-500)解析優化是 Oracle 效能調優的**地基工程**。沒有 Bind Variable 規範,所有索引、分區、並行、Hint 都是空中樓閣。從今天起,將「Bind Variable 覆蓋率 100%」寫入編碼規範、代碼審查清單、上線驗收標準。