oracle将字面量不同的sql视为不同语句,因未使用绑定变量,导致硬解析生成多个sql_id;v$sql中sql_text字符串完全匹配才共享,'1001'与'1002'差异即触发新解析。
awr报告里sql stats部分出现多条几乎一模一样的sql(仅字面量不同,如 where id = 1001、where id = 1002),根本不是数据展示重复,而是oracle把它们当作了**完全不同的sql语句**——因为没用绑定变量。
为什么v$sql里会看到一堆“重复SQL”
Oracle判断SQL是否可共享,看的是完整的sql_text字符串。只要有一个字符不同(比如'1001' vs '1002'),就视为新语句,触发硬解析,生成独立的sql_id和child_number。这些语句在v$sql或AWR的SQL Stats里自然并列出现,看起来像“重复”,实则是散列的硬解析产物。
- 查证方式:运行
SELECT sql_id, sql_text FROM v$sql WHERE sql_text LIKE '%WHERE id = %' AND ROWNUM ,大概率看到5条<code>sql_text只差一个数字 - 关键指标佐证:
v$sql.parse_calls / v$sql.executions接近1,说明几乎每次执行都重新解析 - 不是AWR抽样或汇总错误,是底层共享池行为的真实反映
cursor_sharing=force能自动去重吗
能,但只对硬解析阶段起作用,且有副作用。它会让Oracle在语法分析前,把字面量悄悄替换成系统绑定变量(如WHERE id = :SYS_B_0),从而让原本不同的SQL归为同一个sql_id。
- 启用后,
v$sql中对应SQL数量会明显减少,AWR SQL Stats里“重复项”消失 - 风险:可能改变执行计划——比如
WHERE status = 'A'和WHERE status = 'Z'本应走不同索引,强制绑定后统一走全表扫描 -
cursor_sharing=similar更危险,Oracle官方已明确标注该值存在bug,11g中应避免使用
怎么确认某条SQL到底有没有用绑定变量
别依赖v$sql_bind_capture——它默认关闭,且只捕获最近15分钟内活跃的绑定值,查不到不等于没用。
- 最直接方式:查
v$sql.sql_text字段,找:开头的占位符,如WHERE order_id = :order_id - 验证执行时是否真用了绑定:用
DBMS_XPLAN.DISPLAY_CURSOR('sql_id', child_number),看输出里的Peeked Binds节是否有值 - 如果应用层传的是拼接字符串(如Java里
"WHERE id = " + userId),那肯定没用绑定变量,哪怕SQL文本长得像
真正难的不是发现重复SQL,而是改掉应用里那些"WHERE name = '" + name + "'"式的写法——数据库参数只是兜底手段,治标不治本。一旦业务SQL量上来,硬解析碎片会持续挤占library cache,free memory掉到50MB以下时,连软解析都开始变慢。











