MERGE性能劣化主因是硬解析、锁争用及执行计划不稳;需查ASH中in_parse/in_hard_parse占比、等待事件分布及sql_plan_hash_value波动,结合索引、绑定变量和统计信息优化。

查 MERGE 是否卡在 in_parse 或 in_hard_parse
硬解析是 MERGE 性能劣化的常见元凶,尤其当语句里拼接了字面量(如 USING 子句中写死 WHERE id = 123)而没用绑定变量时。ASH 的 in_parse 和 in_hard_parse 列能直接暴露这个问题。
执行这个查询,聚焦最近 5 分钟内 MERGE 的解析行为:
SELECT in_parse, in_hard_parse, COUNT(*) cnt
FROM v$active_session_history
WHERE sample_time > SYSDATE - INTERVAL '5' MINUTE
AND sql_id IN (
SELECT sql_id FROM v$sql WHERE sql_text LIKE '%MERGE%INTO%WHEN%'
)
GROUP BY in_parse, in_hard_parse
ORDER BY cnt DESC;如果返回中 Y Y 行占比超过 10%,基本可判定是硬解析拖慢了整条 MERGE;此时应优先检查是否漏写了绑定变量,或是否在 PL/SQL 循环中动态拼接 MERGE 语句。
定位 MERGE 的等待事件和热点对象
MERGE 本质是“读 + 判断 + 写”,容易在三个环节卡住:读取源表(db file sequential read)、获取目标行锁(enq: TX - row lock contention)、争用数据块(RAC 下的 gc cr block busy 或 gc current block busy)。
用以下语句把 MERGE 的 ASH 样本按等待事件聚合:
SELECT NVL(event, 'ON CPU') event,
COUNT(*) total_wait_time,
ROUND(RATIO_TO_REPORT(COUNT(*)) OVER () * 100, 2) pct
FROM v$active_session_history
WHERE sample_time > SYSDATE - INTERVAL '10' MINUTE
AND sql_id IN (
SELECT sql_id FROM v$sql WHERE UPPER(sql_text) LIKE '%MERGE%INTO%'
)
GROUP BY event
ORDER BY total_wait_time DESC;重点关注:
- 若
enq: TX - row lock contention占比高,说明目标表存在行级锁冲突,检查是否在 MERGE 的ON条件列上缺少索引,或并发执行多条 MERGE 更新同一主键范围 - 若
gc cr block busy高(RAC 环境),说明源表或目标表的块被频繁构建 CR 副本,通常源于高并发读 + 缺少_gc_read_mostly_locking或热点主键设计 - 若
db file sequential read主导,且current_obj#指向源表,说明USING子句里的驱动表没走索引,需检查执行计划中的访问路径
确认 MERGE 执行计划是否稳定
ASH 不保存完整执行计划,但能通过 sql_plan_hash_value 判断计划是否频繁切换。计划抖动会导致 MERGE 性能忽高忽低,尤其在统计信息过期或绑定变量窥探失效时。
运行:
SELECT sql_plan_hash_value, COUNT(*) cnt
FROM v$active_session_history
WHERE sample_time > SYSDATE - INTERVAL '30' MINUTE
AND sql_id IN (
SELECT sql_id FROM v$sql WHERE UPPER(sql_text) LIKE '%MERGE%INTO%'
)
GROUP BY sql_plan_hash_value
ORDER BY cnt DESC;如果返回多行且最大值占比低于 70%,说明计划不稳定。此时不能只依赖 ASH,必须结合 DBA_HIST_SQL_PLAN 查历史计划变化,并用 DBMS_SQLTUNE.CREATE_SQL_PROFILE 固化可靠计划。
注意 ASH 的采样盲区和保留限制
ASH 是内存环形缓冲区,默认只保留约 1 小时活跃会话样本(具体时长取决于 _ash_size 和负载)。它不记录空闲会话、也不捕获未等待且未消耗 CPU 的瞬间状态。
这意味着:
- MERGE 若执行很快(v$sql_monitor(需开启
MONITORhint 或设置CONTROL_MANAGEMENT_PACK_ACCESS) - 若问题发生在凌晨低峰期,而 ASH 缓冲区被白天流量占满,历史样本可能已被覆盖——此时要立刻查
DBA_HIST_ACTIVE_SESS_HISTORY,它从 ASH 持久化到 SYSAUX 表空间,保留默认 8 天(受STATISTICS_LEVEL和 AWR 保留策略影响) -
sample_time是采样时刻,不是 SQL 开始/结束时间;单次 MERGE 若耗时 5 秒,理论上最多被采到 5 次,但实际可能只有 1–2 次,不能靠计数反推执行时长
真正关键的 MERGE 瓶颈,往往藏在 ASH 看不见的地方:比如硬解析后的库缓存争用(library cache: mutex X),或者并行 MERGE 中 QC 节点的串行瓶颈——这些得配合 AWR 的 Top 5 Timed Events 和 Instance Activity Stats 交叉验证。



















