小鱼(邓秋爽) 云和恩墨专家,有超过5年超大型数据库专业服务经验,擅长oracle 数据库优化、SQL优化和troubleshooting 这是我们在做系统割接的时候的一个案例,可能并不是很常见,这个案例是将Oracle 11g升级到12c的时候遇到的问题,出现了大量的library cache的问题。具体情况是: 新系统割接后,不定时出现大量library cache lock、library cache:mutex X,几分钟后系统自动恢复。 在短暂的时间里,我们来不及做systemdump的,而且出现的频率和时间也是不固定的,很难抓取到当时系统的信息。 先获取故障时段的AWR报告:
我们从以下几个角度分析:
在oracle 10g的时候,V$SQLAREA视图有一个FORCE_MATCHING_SIGNATURE 参数,可以将SQL经过绑定变量代替后生成一个hashvalue值,通过这个值找到未使用绑定变量的SQL,而开发商的SQL的质量比较高,并未发现核心业务SQL未使用绑定变量的情况。 这样看来,这个问题是很棘手的,硬解析次数很高,但我们找不到对应的SQL在哪里。 我们接着分析,来看AWR报告里面的time model statistic 我们看到红色标记的部分,解析时间消耗了63.74%、解析失败消耗了50.55%。 解析失败是什么?Oracle的解释是这样的: failed parse:语法、权限等无法执行的SQL解析,也是硬解析,并且解析失败是不能被重用的,当然它也不会存储在V$SQLAREA视图中,所以也挖掘不到这类SQL。 我们如何去发现在系统中解析失败的SQL呢? Oracle提供了event 10035,会将解析失败的SQL记录到alert 日志里面 从上面的日志可以看到各种解析错误的代码,其中error=942,表示:表不存在,因此判断这是他们做系统变更的时候做过一些表的删除,我们可能在系统割接的时候都会做一些旧表的drop或者rename,这时候一定要严格挖掘应用端的代码,将下线的业务代码停掉,避免错误解析导致数据库出现严重的性能问题。 接下来和大家分享执行计划结合业务逻辑的一个等价改写的例子 案例中的SQL如上,大致由两部分组成,上下各是一个标量子查询,然后用union all联合在一起做了一个order by,在结果显示中使用了分页。 我们通过脚本获得该SQL单次逻辑读将近18000000.返回行数为10行,响应时间达到104036MS。 这是个很复杂的SQL,包含标量子查询、表连接、unionall、排序、分页,还有一些复杂的decode、nvl等函数,通过awr报告我们得知该SQL单次执行需要1500多万到1900多万的逻辑读,平均都只返回10行数据,单次执行时间也要100秒左右。 我们可以将SQL简化如下:
对于这种复杂的SQL,我们先看执行计划 这个执行计划我们做过相应删减。
在上面的标量子查询中,Cost消耗最高的在这个view操作,COST消耗达到了14M、rows达到了501K,而这个view是由两部分union all组成的。 在下面的标量子查询中,两部分union all发现上层部分主查询MM_MK_CUSTMGR_SIGN T估算返回501k Rows,下层主查询则只有1Rows数据。 注:在Oracle的估算中是不存在0 Rows的情况,如果评估的结果是0,会算作1. 对于标量子查询,我们简单做个介绍,就是说优化器在这种情况下永远只做一种操作就是filter,这是一种变相优化nest loop。对于这种标量自从查询,我们知道其实SQL之所以出现问题是因为下面的501k导致需要驱动上面那堆复杂的标量子查询, 那么如何优化呢?
接下来主要针对第二种,结合业务进行分析改写。 在上面的SQL中,是先取501k数据做了驱动,然后再做标量子查询和order by的操作,我们能不能把order by的操作推回到标量子查询前面,这样子的话标量子查询要驱动的只是前面排序取rownum限制条件的数据,我们通过画图的方式来分析一下: 首先是两个同样的表,做了标量子查询的操作,这里的数据是501k,然后标量子查询完了之后,做了order by后rownum的限制,这是原SQL的执行业务逻辑。 我认为应该写成这样,我们想限制标量子查询的循环次数,那我们就先去对主查询取order by排序rownum限制后的数据,再将主查询取出来的这部分数据去驱动标量子查询,做完后再做一次order by rownum的限制。(这里并不会改变SQL的业务逻辑,虽然我们是先排序取rownum限制了,但是标量子查询时主查询是先排序还是后排序取rownum限制对于主查询返回结果集没有任何影响) 根据这种思路,我把SQL改写如下:
其中红色部分是12C的改写方式。是用一个分析函数的方式去做的。 它的执行计划如下: 先访问表MM_MK_CUSTMGR_SIGN排序取rownum限制(前10行数据后),再去驱动那堆复杂的标量子查询,最后再次排序取rownum条件数据,逻辑读从千万级降低到了26661。 这个SQL在改写后,资源消耗降低了许多,基本上能够满足业务的需求。 如果我们再去剖析原SQL代码,发现union all部分是同一个MM_MK_CUSTMGR_SIGN表的查询,下面那个UNION ALL部分查询出来的结果是上面UNION ALL部分的子集。 而跟研发沟通发现实际上union all的下层查询可以去掉,去掉后则该SQL无需改写rownum就可以直接推进到主查询中,从这个例子可以看到不严谨的代码容易造成性能隐患,影响优化器评估最合理的执行计划。 通过以上分享,我们得出:
|
|