本系列第六部分聚焦實戰 SQL 改寫模式、低效 SQL 識別查詢、TKPROF 追蹤分析,幫助 DBA 與開發人員快速定位並解決效能瓶頸。
原則: 在 RBO (Rule-Based Optimizer) 或早期 CBO 版本中,JOIN 常比 EXISTS 效率高,因後者執行路徑包含 FILTER 操作,前者可使用 NESTED LOOPS。
SELECT ename
FROM emp e
WHERE EXISTS (
SELECT 'X'
FROM dept d
WHERE d.deptno = e.deptno
AND d.dept_cat = 'A'
);
SELECT e.ename
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE d.dept_cat = 'A';
現代 CBO (11g+) 建議: 優化器會自動轉換 EXISTS 為 SEMI JOIN,兩者效能通常等價。若 EXISTS 子查詢極其複雜,改寫為 JOIN 並加上 DISTINCT 或 /*+ NO_UNNEST */ Hint 可能更穩定。
場景: 一對多關聯查詢 (部門-員工) 需去重部門資訊。
SELECT DISTINCT d.deptno, d.dname
FROM dept d
JOIN emp e ON d.deptno = e.deptno;
SELECT d.deptno, d.dname
FROM dept d
WHERE EXISTS (
SELECT 'X'
FROM emp e
WHERE e.deptno = d.deptno
);
原理: EXISTS 找到第一筆符合即停止掃描 (Short-circuit),避免 DISTINCT 的排序/雜湊去重開銷。CBO 會轉為 HASH JOIN SEMI 或 NESTED LOOPS SEMI。
從 V$SQLAREA 找出緩衝區命中率低、單次執行讀取量大的 SQL:
SELECT executions,
disk_reads,
buffer_gets,
ROUND((buffer_gets - disk_reads) / buffer_gets, 2) AS hit_ratio,
ROUND(disk_reads / executions, 2) AS reads_per_exec,
sql_text
FROM v$sqlarea
WHERE executions > 0
AND buffer_gets > 0
AND (buffer_gets - disk_reads) / buffer_gets < 0.8 -- 命中率 < 80%
ORDER BY hit_ratio ASC, reads_per_exec DESC;
指標解讀:
hit_ratio < 0.8:大量物理讀,可能缺乏索引、全表掃描、統計資訊過期。reads_per_exec > 1000:單次執行讀取過多區塊,需檢視執行計畫。sql_id 進一步分析:SELECT * FROM v$sql_plan WHERE sql_id = '...';-- Session 級 (建議)
ALTER SESSION SET SQL_TRACE = TRUE;
ALTER SESSION SET TIMED_STATISTICS = TRUE; -- 必須開啟,否無時間統計
ALTER SESSION SET MAX_DUMP_FILE_SIZE = UNLIMITED;
-- 執行目標 SQL...
-- 關閉追蹤
ALTER SESSION SET SQL_TRACE = FALSE;
-- 系統級 (慎用,影響效能)
-- init.ora: sql_trace = true, timed_statistics = true, user_dump_dest = '/path'
# 找到 trace 檔 (user_dump_dest 目錄下 *.trc)
tkprof orcl_ora_12345.trc orcl_ora_12345.txt \
EXPLAIN=scott/tiger \
TABLE=scott.temp_plan_table \
SYS=NO \
SORT=EXEELA,FCHELA
常用排序選項: PRSCNT (解析次數)、EXECNT (執行次數)、FCHCNT (擷取次數)、EXEELA (執行耗時)、FCHELA (擷取耗時)、QRCNT (查詢區塊數)。
| 欄位 | 意義 |
|---|---|
| COUNT | 解析/執行/擷取 次數 |
| CPU | CPU 時間 (秒) |
| ELAPSED | 經過時間 (秒) |
| DISK | 物理讀區塊數 |
| QUERY | 一致性獲取 (邏輯讀) |
| CURRENT | 當前獲取 (DML 相關) |
| ROWS | 處理列數 |
TABLE ACCESS FULL、INDEX RANGE SCAN、HASH JOIN 實際啟動次數與列數。db file sequential read (索引讀)、db file scattered read (全表掃描)、log file sync (Commit 等待)。| 工具 | 用途 | 啟用方式 |
|---|---|---|
| SQL Monitoring | 即時監控長時間 SQL (DB_TIME > 5s) | 自動啟用,V$SQL_MONITOR, DBMS_SQLTUNE.REPORT_SQL_MONITOR |
| Real-time SQL Monitoring | 執行中 SQL 進度、並行執行狀態 | /*+ MONITOR */ Hint 或自動 |
| ADS (Automatic Database Diagnostic Monitor) | 自動分析 AWR 快照,找出 Top SQL、瓶頸 | 預設啟用,DBMS_ADVISOR |
| ASH (Active Session History) | 每秒採樣 Session 狀態,事後分析等待 | V$ACTIVE_SESSION_HISTORY, DBA_HIST_ACTIVE_SESS_HISTORY |
| SQL Tuning Advisor | 自動建議索引、Profile、改寫 | DBMS_SQLTUNE, Enterprise Manager |
建議:建立標準化「SQL 效能檢核清單」納入 Code Review 流程:1. 有無全表掃描 2. 索引是否被使用 3. 統計資訊是否最新 4. Bind Variable 是否一致 5. 執行計畫是否穩定 (SQL Plan Baseline)。