AWR中SQL Parse Time占比异常高(如超15%)基本表明硬解析泛滥,主因是未使用绑定变量,导致重复语法检查、权限验证和执行计划生成;v$sqlarea.parse_calls与executions比值近1、Parse CPU与Elapsed接近、v$sql中无冒号占位符等均为关键佐证。
awr报告里看到 sql parse time 占比异常高(比如超过15%),基本可以断定是硬解析泛滥,不是sql本身慢,而是每次执行都在重复做语法检查、权限验证、执行计划生成——这90%以上都和没用绑定变量有关。
为什么AWR里SQL Parse Time会虚高
AWR的 SQL Parse Time 指标统计的是所有硬解析 + 软解析的总耗时,但它不区分类型。而真实瓶颈几乎全在硬解析上。当大量字面量SQL涌入(如 WHERE order_id = 1001、WHERE order_id = 1002),Oracle认为这是两条不同语句,每条都触发硬解析,CPU狂转,library cache latch 等待飙升,最终在AWR里表现为“解析时间长”。
- 硬解析一次平均消耗 0.5–2ms CPU(实测值),1000tps下若90%未绑定变量,每秒新增900个硬解析,光解析就吃掉450ms+ CPU时间
-
v$sqlarea.parse_calls和v$sqlarea.executions比值若接近1,说明几乎每次执行都在重新解析 - AWR的
Parse CPU和Parse Elapsed差值不大,说明不是I/O卡住,而是纯CPU密集型开销
v$sql_bind_capture 查不到绑定信息?先确认捕获开关
想查某条SQL是否用了绑定变量,别急着翻 v$sql_bind_capture ——它默认是关闭的,且只捕获最近15分钟内活跃的绑定值(受 _cursor_bind_capture_area_size 限制)。直接查可能为空,不代表没用绑定变量。
- 检查开关:运行
SELECT name, value FROM v$parameter WHERE name = 'cursor_bind_capture_destination',值必须是memory或both - 启用捕获(需DBA):
ALTER SYSTEM SET "_cursor_bind_capture_area_size" = 1048576 SCOPE=SPFILE,再重启实例 - 更可靠的方式是查
v$sql的sql_text字段,看是否有:var_name占位符;或用DBMS_XPLAN.DISPLAY_CURSOR(sql_id, child_number)看Peeked Binds部分
共享池配置不能只调shared_pool_size
盲目增大 shared_pool_size 只会让问题更隐蔽:硬解析照旧,只是共享池淘汰慢一点,反而掩盖了SQL写法缺陷。真正要动的是结构化复用能力。
- 优先确保
cursor_sharing设置为FORCE(开发环境慎用,可能改执行计划),它能自动把字面量转成绑定变量(如WHERE id = 123→WHERE id = :1) - 检查
v$sgastat中library cache和sql area的内存占比,若free memory长期低于 50MB,说明硬解析碎片太多,不是缺内存,是SQL太散 -
shared_pool_reserved_size应设为shared_pool_size的5%~10%,专供大SQL解析使用,避免小SQL挤占导致大SQL硬解析失败
PL/SQL块里用绑定变量最稳妥
应用层ORM(如MyBatis、Hibernate)有时会因缓存策略或动态SQL拼接绕过绑定,而PL/SQL过程天然支持变量绑定,且编译时就固化执行计划,是最可控的落地方式。
- 不要这样写:
EXECUTE IMMEDIATE 'SELECT * FROM orders WHERE cust_id = ' || v_id; - 应该这样写:
EXECUTE IMMEDIATE 'SELECT * FROM orders WHERE cust_id = :id' USING v_id; - 批量操作更明显:用
BULK COLLECT INTO+FORALL ... USING,一次绑定、批量执行,避免循环里反复硬解析 - 注意:PL/SQL中变量名和SQL里的绑定名不必一致(
:id和v_id是独立的),但顺序和类型必须严格匹配
绑定变量不是“加个冒号就完事”,关键在让Oracle认定SQL文本完全一致——空格、大小写、注释、分号位置,差一丁点都会触发硬解析。很多性能问题藏在开发习惯里,而不是配置参数里。


















