游乐游手机版
首页/数据库/文章详情

如何通过Oracle 19c AWR定位硬解析过高SQL

时间:2026-06-24 07:46
通过AWR中ExecutetoParse%低于90%判断绑定变量使用不足,结合ASH验证latch:sharedpool争用;再基于SQLorderedbyParseCalls及DBA_HIST_SQLSTAT计算parse_ratio>0 9的语句,排除干扰项后,用ASH抓取当前争用latch的活体SQL,最终精准定位硬解析过高的SQL语句,以优化性能。

如何通过Oracle 19c AWR定位导致硬解析过高的SQL语句

先说几个核心判断:AWR中Execute to Parse%低于90%时,基本可以断定绑定变量使用不足。但别急着下结论,还需要配合ASH进一步验证,确认是否真的存在latch: shared pool争抢。此外,那些parse_ratio超过0.9的SQL语句,必须仔细排查是缺少绑定变量、字面量频繁变化,还是NLS参数出现了漂移。

直接看AWR里的Execute to Parse%是否跌破90%

判断绑定变量是否充分利用,最直接的方法就是打开AWR报告,查看Execute to Parse %指标。该百分比位于instance efficiency percentages表格中,Oracle已经自动计算,无需手动统计。数值越低,说明解析占比越高,绑定变量使用效果越差。如果连续多份报告都低于90%,基本可以确定是应用层遗漏了绑定变量,而非偶然的性能抖动。

如何通过Oracle 19c AWR定位导致硬解析过高的SQL语句

别被名称误导:Execute to Parse % 并非指“执行一次再解析一次”,而是「平均每执行100次,仅解析几次」的倒推值。95%意味着每100次执行只解析5次;80%则意味着每100次执行需要解析20次。这样对比下来,差距显而易见。

重点在于时段对比。例如,将业务高峰时段(上午9:30–10:30)的报告与非高峰时段(凌晨2:00–3:00)进行对比。如果仅高峰时段出现暴跌,说明压力触发了游标失效或子游标膨胀,不一定是代码本身的问题。

从SQL ordered by Parse Calls查找高频解析语句

翻到AWR报告的SQL StatisticsSQL ordered by Parse Calls部分,重点关注前5条记录。真正危险的不是Parse Calls最高的那条,而是那些ParsesExecutions都很高的SQL——例如Parse Calls=12000,Executions=15000,比值接近1,这几乎意味着每次执行都在进行硬解析。

快速验证方法如下:

  • 复制该sql_id,查询v$sqlSELECT sql_text, executions, parse_calls FROM v$sql WHERE sql_id = 'xxx'
  • 如果executions = 1parse_calls > 1,说明该语句反复硬解析后被挤出共享池,这是典型的碎片化信号
  • 如果sql_text中包含WHERE id = 123这样的固定值,而非WHERE id = :b1,那就是绑定变量缺失的有力证据

用DBA_HIST_SQLSTAT算parse_ratio排序

AWR本身不存储解析类型标记,但DBA_HIST_SQLSTAT中包含parse_callsexecutions字段,可以间接反映游标复用程度。执行以下查询(记得替换快照范围):

SELECT sql_id, sql_text, parse_calls, executions,
       ROUND(parse_calls / NULLIF(executions, 0), 2) parse_ratio
FROM dba_hist_sqltext t
JOIN dba_hist_sqlstat s USING (sql_id)
WHERE s.snap_id BETWEEN &begin_snap AND &end_snap
  AND s.parse_calls > 50
  AND s.executions > 0
ORDER BY parse_ratio DESC, s.parse_calls DESC;

parse_ratio > 0.9的SQL基本上意味着游标未被重用。但需要过滤掉DBA工具类语句(例如SELECT * FROM v$session),这些语句本身就不适合进入库缓存。

常见的干扰项值得注意:

  • INSERT INTO ... VALUES (:1, :2, to_date(:7, 'yyyymmddhh24miss')) —— 看似使用了绑定变量,但如果:7的字符串长度每次不同(比如'20260612123456' vs '20260612123'),Oracle会认为字面量不等价,强制创建新的子游标
  • UNBOUND_CURSOR = 'Y'v$sql_shared_cursor中为真,说明Oracle明确拒绝复用——不是你代码写错,而是环境参数(例如NLS_LENGTH_SEMANTICS)发生了漂移

光看AWR不够,必须配合ASH抓取正在抢latch的活体SQL

AWR是历史汇总,ASH才是实时快照。latch: shared pool高,本质是硬解析卡在获取latch上,不是结果,而是正在发生的争抢。用以下语句抓取最近1分钟的活体SQL:

SELECT sql_id, sql_opname, COUNT(*) cnt
FROM v$active_session_history
WHERE event = 'latch: shared pool'
  AND sample_time > SYSDATE - 1/1440
GROUP BY sql_id, sql_opname
ORDER BY cnt DESC
FETCH FIRST 10 ROWS ONLY;

有几个关键点:

  • 务必添加SAMPLE_TIME > SYSDATE - 1/1440,否则默认查询全量历史,速度慢且噪声大
  • sql_opname分组(如SELECTINSERT),避免将DML和查询混在一起导致误判
  • 获取sql_id后,立即查询v$sqlloadsexecutions:如果executions = 1loads > 5,说明每次值变化都会触发重编译

最容易被忽视的一点是:许多人查到高parse_ratio的SQL就急着去修改代码,却没有确认它是否真的在抢latch: shared pool。可能只是统计信息过期导致优化器反复生成新执行计划,而latch争用来自另一批完全不同的语句。这种情况下,ASH才是唯一能交叉验证的数据来源。

来源:https://www.php.cn/faq/2677503.html
上一篇SQL中LIKE通配符模糊搜索正确用法与性能优化 下一篇SQL触发器实现数据录入自动生成唯一业务流水号
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Redis 7.0增量AOF重写RDB前导码配置详解
数据库 · 2026-07-02

Redis 7.0增量AOF重写RDB前导码配置详解

先说一个几乎所有人都踩过的典型误区:很多人把 aof-use-rdb-preamble yes 当作开启“增量重写”的开关。实际上,这个配置只干了一件事——让重写后的 AOF 文件头部带上 RDB 快照。它解决的是加载速度问题,跟“增量重写”本身的概念压根不是一回事。真正的增量重写,依赖的是 Red

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践
数据库 · 2026-07-02

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践

直接在Tornado里用SQLAlchemy同步执行SQL,结果就是阻塞IOLoop,所谓“异步框架里写同步数据库代码”,等于白搭。安全执行的关键不是“怎么写SQL”,而是“怎么不卡住事件循环”。 为什么不能在RequestHandler里直接调用session execute() 因为sessio

利用SQL触发器实现在INSERT数据时自动同步到审计表
数据库 · 2026-07-02

利用SQL触发器实现在INSERT数据时自动同步到审计表

先说结论:可以用触发器把 INSERT 数据同步到审计表,但必须用 AFTER INSERT,并且审计表的字段顺序、类型、字符集得和源表严格一致。否则,轻则写入错位、数据截断,重则直接报错、丢数据。下面把这些坑一个一个掰开说。 能,但必须用 AFTER INSERT,且审计表字段顺序、类型、字符集要

如何用SQL编写按不同工作日统计员工出勤率
数据库 · 2026-07-02

如何用SQL编写按不同工作日统计员工出勤率

在实际业务中,统计不同工作日的出勤率是HR系统里的高频需求。如果直接按日期函数分组,很容易掉进语言环境、索引失效或分母口径的坑里。下面就来拆解具体的实现要点。 必须用 CASE WHEN 将日期映射为固定 weekday 标签(如 Mon )再分组,避免语言环境导致的分组断裂;需过滤 DOW IN

Spring Boot 3动态拼接SQL为何引发严重安全漏洞
数据库 · 2026-07-02

Spring Boot 3动态拼接SQL为何引发严重安全漏洞

SQL注入漏洞的核心成因,本质上是因为用户输入直接参与了SQL语句的字符串拼接,而未采用参数化绑定机制。在MyBatis中使用${}、QueryWrapper中调用apply()与last()、JPA的@Query注解进行拼接等操作,都会绕过PreparedStatement的安全防护。动态字段必须