on cpu高但sql执行快,说明cpu消耗在非执行路径,需查v$session(state/sql_id为空则为硬解析或pl/sql开销)、v$sql(parse_calls远大于executions为硬解析风暴)、v$sql_shared_cursor(bind_mismatch/unbound_cursor=y表明游标无法共享)。
oracle 12c 中会话标为 on cpu,但对应 sql 执行时间却远低于预期(比如 elapsed_time 平均仅几毫秒),说明问题不在 sql 本身执行慢,而在于 cpu 时间被非纯执行逻辑大量占用——常见于硬解析、pl/sql 循环开销、递归调用或绑定变量窥探抖动。
查 ON CPU 样本时为什么不能只看 sql_id?
ASH 的 sql_id 字段在 ON CPU 状态下可能指向“顶层容器”,而非真实消耗点:
- PL/SQL 过程中循环执行
SELECT ... BULK COLLECT INTO,sql_id记录的是过程名(如04x9mz8kq7v2f),不是内部查询的sql_id - 硬解析卡住时,
sql_id是待解析语句,但样本实际耗在library cache lock或row cache lock的 CPU 持有上,而非执行 - 使用了
/*+ MONITOR */的 SQL 若未满足 >5 秒阈值,不会进V$SQL_MONITOR,但其解析/优化阶段仍会占ON CPU样本
ON CPU 高但 v$sql.elapsed_time/executions 很低,下一步查什么?
这说明 CPU 消耗集中在“非执行路径”。重点交叉验证以下三类视图:
- 查
V$SESSION当前状态:SELECT sql_id, sql_child_number, state, event, p1text, p1 FROM v$session WHERE sid = <sid></sid>—— 若state = 'ON CPU'但sql_id IS NULL,大概率是硬解析或 PL/SQL 解释器开销 - 查
V$SQL解析统计:SELECT sql_id, parse_calls, loads, invalidations FROM v$sql WHERE sql_id = '<target_sql_id>'</target_sql_id>——parse_calls >> executions是硬解析风暴信号 - 查
V$SQL_PLAN是否含OPTIMIZER_STATS或RECURSIVE EXECUTION操作符,这类计划节点常触发隐式递归调用,CPU 耗在内核函数而非 SQL 扫描
如何确认是不是 PL/SQL 循环或绑定变量导致的 CPU 毛刺?
PL/SQL 内部逻辑无法通过 ASH 直接看到子 SQL,但可通过间接指标定位:
- 对目标
sql_id执行:SELECT sql_plan_hash_value, COUNT(*) FROM v$active_session_history WHERE sql_id = '<id>' AND session_state = 'ON CPU' GROUP BY sql_plan_hash_value</id>—— 若返回多个不同sql_plan_hash_value,说明执行计划不稳定,极可能是绑定变量窥探 + 数据倾斜触发反复硬解析 - 查
V$SQL_SHARED_CURSOR:SELECT * FROM v$sql_shared_cursor WHERE sql_id = '<id>' AND (UNBOUND_CURSOR = 'Y' OR BIND_MISMATCH = 'Y')</id>—— 出现Y表示共享游标失效,每次执行都走新解析路径 - 检查 PL/SQL 单元是否含
EXECUTE IMMEDIATE或动态 SQL 字符串拼接 —— 这类代码天然无法重用游标,ON CPU样本会集中在kkocsc(cursor compile)函数栈
真正细粒度的 CPU 消耗分析,不能只盯 V$ACTIVE_SESSION_HISTORY。当发现 ON CPU 异常但 SQL 执行快时,V$SQL 的解析统计、V$SQL_SHARED_CURSOR 的游标不共享原因、以及 PL/SQL 单元是否规避了绑定变量复用,这三点比调大 _ash_size 或改采样间隔更关键。











