前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >记一个Enq: ss - contention性能问题处理

记一个Enq: ss - contention性能问题处理

作者头像
老虎刘
发布2022-06-22 17:58:07
发布2022-06-22 17:58:07
1K0
举报

最近在忙上海南京广州深圳为ACS客户做技术分享,没太多时间更新公众号。接下来我会把这次技术分享里面一些案例写在公众号。今天是第一篇。

客户报业务处理突然变得非常慢,大量业务堆积,需要紧急处理。AWR显示主要等待事件为Enq: ss - contention:

根据Oracle 文档对该事件的描述:

When SMON is cleaning up very large dead transactions, it may not service other work like cleaning up temporary space, handling sort-segment requests or performing instance recovery.

检查数据库,果然存在一个大的update事务在回滚,而且预计还有很长一段时间才能回滚完成。

这种情况下,如何恢复当前业务的执行效率呢?

1、加速回滚,事务回滚完SMON就能正常管理sort segment了;

2、手工清理临时表空间:

alter session set events 'immediate trace name drop_segments level <TS number + 1>;

这两种方法是一个正常DBA的思维,非常正确。不过下次再遇到大事务回滚,还是会出现相同问题。

客户的运维DBA同时提供了一个受影响业务的SQL,并收集了sqlhc(SSC的客户都养成了这个好习惯),经过简单分析,发现这个问题从SQL优化的角度来解决效果会更佳。

对应的SQL,代码如下:

WITH TMP_RESULT AS

(SELECT A.ACCT_ID,

A.PRD_INST_ID,A.OFR_ID,

A.FEE_TYPE,A.ACCT_ITEM_TYPE_ID,

B.ACCT_ITEM_TYPE_NAME,A.FLUX,

A.RATE_DURATION,A.CHARGE

FROM V_DAT_M_21 A,

TB_BIL_ACCT_ITEM_TYPE B,

TB_PRD_PRD_INST_21 D

WHERE D.SERVICE_NBR = '888'

AND D.PRD_INST_ID = A.PRD_INST_ID

AND A.ACCT_ITEM_TYPE_ID = B.ACCT_ITEM_TYPE_ID

)

SELECT C.ACCT_ID,C.PRD_INST_ID,

C.OFR_ID,E.OFR_NAME,

C.FEE_TYPE,C.ACCT_ITEM_TYPE_ID,

C.ACCT_ITEM_TYPE_NAME,C.FLUX,

C.RATE_DURATION,C.CHARGE

FROM TMP_RESULT C, TB_PRD_OFR E

WHERE C.OFR_ID = E.OFR_ID AND C.OFR_ID <> 0

UNION

SELECT C.ACCT_ID,C.PRD_INST_ID,

C.OFR_ID,NULL OFR_NAME,

C.FEE_TYPE,C.ACCT_ITEM_TYPE_ID,

C.ACCT_ITEM_TYPE_NAME,C.FLUX,

C.RATE_DURATION,C.CHARGE

FROM TMP_RESULT C

WHERE C.OFR_ID = 0;

执行计划如下:

这种使用with的CTE(Common Table Expression)写法的SQL,在with对象被引用两次以上时,会先对命名对象生成一张临时表(如果包含字段包含lob字段则不会)写到临时表空间,这个过程叫materialize,后续使用该对象的时候直接从临时表中读取数据,不需要重复执行这段SQL。

这个SQL频繁执行,因此在SMON繁忙的时候产生了大量的Enq: ss - contention等待事件,执行效率由原来的1秒多,变成了当前的20秒,性能下降将近20倍。而其他SQL则几乎没有受到影响。

我们来分析一下这个SQL为什么会使用CTE:因为SQL用到的一个view:V_DAT_M_21,这个view是从db link获取数据,这个步骤消耗的时间最长,使用CTE的materialize就是为了避免这个view的两次调用。本来一个为了提高效率的优化操作,遇到了大事务回滚反而性能下降了很多。

分析到这里,我们又多了一个解决方案:用/*+ inline */hint ,告诉优化器不要做materialize,这样就避免了临时表的生成和临时表空间的使用,执行时间可能由1秒提高到2秒,但是也远比20秒要好很多。这个操作可以通过sql profile实现,不需要修改SQL代码。

我们再来看一下SQL逻辑,发现这个SQL可以通过一个外关联的等价改写来实现:

WITH TMP_RESULT AS

(SELECT A.ACCT_ID,A.PRD_INST_ID,

A.OFR_ID,A.FEE_TYPE,A.ACCT_ITEM_TYPE_ID,

B.ACCT_ITEM_TYPE_NAME,A.FLUX,

A.RATE_DURATION,A.CHARGE

FROM

V_DAT_M_21 A,

TB_BIL_ACCT_ITEM_TYPE B,

TB_PRD_PRD_INST_21 D

WHERE

D.SERVICE_NBR = '888'

AND D.PRD_INST_ID = A.PRD_INST_ID

AND A.ACCT_ITEM_TYPE_ID = B.ACCT_ITEM_TYPE_ID

)

SELECT distinct C.ACCT_ID,C.PRD_INST_ID,C.OFR_ID,

DECODE(C.OFR_ID ,0 , null , E.OFR_NAME ) as OFR_NAME,

C.FEE_TYPE,C.ACCT_ITEM_TYPE_ID,

C.ACCT_ITEM_TYPE_NAME,C.FLUX,

C.RATE_DURATION,

C.CHARGE

FROM TMP_RESULT C, TB_PRD_OFR E

WHERE C.OFR_ID = E.OFR_ID(+) ;

经过改写后,之前的几个方法就显得多余了。这个SQL既提升了执行效率,又完全不使用temp表空间,再也不需要担心大事务回滚的影响了。

总结:

一个问题可能有多个解决方法,深入分析,多思考一下,可能会发现更好的解决方法。SQL优化,带来的收益往往会超出预期。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-09-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老虎刘谈oracle性能优化 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档