生产环境sql语句调优实战第八篇

生产环境中的sql语句执行时间是很关键的性能指标,如果某个sql语句执行几个小时,优化以后几分钟,几十秒的话。会有很大的成就感,同时如果某个sql语句执行10秒,能够优化到1秒,感觉提升的幅度不是很大,但是如果这条语句执行极为频繁的话,那这种调优还是更有成就感的。
执行时间是sql调优的一把标尺,但是同时也需要考虑到系统资源的平衡。
今天在系统中发现一条sql语句执行时间很长。平均一个查询要执行一个半小时左右,而且系统的资源消耗极大。
需要说明的service_details 是数据量过亿的表。ch_distribute 是千万级的表,subscriber是百万级的表。
payment是千万级的表,paychannel是百万级的表。
查看执行计划,倒看不出有明显的异常,这也就是执行计划的一个误区了,我们不能总是参考执行计划来进行调优,很多时候发现执行计划几乎是完美的,但是执行效率却很长。

sql语句如下所示。
SELECT cd.target_pcn, se.agreement_no, s.subscriber_no, s.prim_resource_val
  FROM ch_distribute CD, service_details SE, subscriber S
 WHERE cd.target_pcn IN
       (SELECT 
         cp.pym_channel_no
          FROM paychannel cp, payment pym
         WHERE cp.pym_channel_no IN
               (SELECT cd.target_pcn
                  FROM ch_distribute cd
                 WHERE (cd.agreement_no, cd.soc, cd.soc_seq_no) IN
                       (SELECT sg.agreement_no, sg.soc, sg.soc_seq_no
                          FROM service_details sg
                         WHERE sg.soc_status = 'A'
                           AND sg.agreement_no IN
                               (SELECT 
                                 sg.agreement_no
                                  FROM service_details sg, subscriber s
                                 WHERE s.subscriber_no = sg.agreement_no
                                   AND sg.soc = 50412
                                   AND sg.soc_status = 'A'
                                   AND sg.soc_sts_rsn_cd =
                                       (SELECT param_values
                                          FROM small_table
                                         WHERE param_name =
                                               'XXXXXXXX1'
                                           AND job_name = 'XXXXXX')   --bottleneck 
                                   AND s.subscriber_type IN
                                       (SELECT param_values
                                          FROM small_table
                                         WHERE param_name = 'XXXXXXXX2'
                                           AND job_name = 'XXXXXX')
                                           ))
                   AND expiration_date IS NULL)
           AND cp.ban = pym.account_id
           AND (pym.transaction_id >
               (SELECT param_values
                   FROM small_table
                  WHERE param_name = 'XXXXXXXX3'
                    AND job_name = 'XXXXXX') AND
               pym.transaction_id
   AND se.agreement_no = cd.agreement_no
   AND s.subscriber_no = se.agreement_no
 GROUP BY cd.target_pcn,
          se.agreement_no,
          s.subscriber_no,
          s.prim_resource_val
          

猛一看这个查询语句还是挺臃肿的,可以明显的看到反复引用了大表service_details,chg_distribute.
我先把这个问题发给一个性能调优的哥们,他在不改动sql语句的前提下,加了几个Hint,执行时间就从1个半小时降低到4分钟左右,猛一看这是一个极大的提升,看似不用修改sql语句了。
我看了下他建议的hint,从执行时间来说,是很大的提升,但是从系统的资源消耗来看,还存在一定的隐患,建议的Hint如下:
SELECT /*+parallel(pym,4) full(pym) use_hash(pym)*/
         cp.pym_channel_no

SELECT /*+PARALLEL(S,4) full(S) FULL(SG) PARALLEL(SG,4) USE_HASH(S,SG)*/
                                 sg.agreement_no
                                  FROM service_details sg, subscriber s

两个Hint本身也没有什么问题,对于大表的关联用hash_join效率比nested loop要高很多。加上并行,如果查询执行不够频繁,涉及的表不多,确实是很好的选择。
我个人的观点还是从语句本身入手,先来看看有什么可以从结构中的改进,先在头脑中有一个基本的思路,然后主要查找数据的性能瓶颈到底在哪,因为根据在备份库上的测试,这个查询返回的数据条数在几千条左右,从上亿条,上千万的数据中排查出几千条肯定是有一些关键的过滤条件。
使用并行固然好,如果在不使用并行的条件下,高效的使用索引是更好的选择。如果实在条件所限,对个别做表全表扫描速度也是很快的。
在分析了数据的统计信息,索引情况之后,在备份库中进行了简单的数据筛查。
首先定位了性能瓶颈,是如下的这个查询条件。通过如下的条件能够过滤掉99%以上的数据,剩下的数据和其它大表关联,都是可以使用到索引的,速度就会快很多。
select *from agreement_no from service_details  sg
        where 
     sg.soc = 50412 and sg.soc_status = 'A' 

有了这个思路,修改起来就轻松多了.
有了主要的改进,其他的改进就可以锦上添花了。
还有两个需要修改的部分。
一个是简化sql语句的表关联,可以看到很多的表出现了多次,这对查询本身来说也不是必须的,个人认为这个sql语句是在开发人员边开发,变修改导致了查询语句嵌套了很多重复的关联。
一个是关于子查询的优化。有几个子查询会关联到一个小表,对小表中的数据进行反复关联。这对子查询而言,执行频率是极高的。
SELECT param_values
                                          FROM small_table
                                         WHERE param_name =
                                               'XXXXXXXX1'
                                           AND job_name = 'XXXXXX'
对于这种特别的子查询,可以考虑使用with语句来替代。改进后的语句如下,这样看就清晰多了。
with ssrc as (SELECT param_values
                   FROM small_table
                  WHERE param_name = 'XXXXXXXX1'
                    AND job_name = 'XXXXXX') ,
       sub_type as (SELECT param_values
                   FROM small_table
                  WHERE param_name = 'XXXXXXXX2'
                    AND job_name = 'XXXXXX'),
      hr_pay_trx as (SELECT param_values
                   FROM small_table
                  WHERE param_name = 'XXXXXXXX3'
                    AND job_name = 'XXXXXX')     
SELECT
           cp.pym_channel_no,sg.agreement_no, s.subscriber_no, s.prim_resource_val
           FROM service_details sg, subscriber s,chg_distribute chg,paychannel cp,sub_type,ssrc
          WHERE s.subscriber_no = sg.agreement_no
            AND sg.soc = 50412
            AND sg.soc_status = 'A'
            AND sg.soc_sts_rsn_cd =
                ssrc.param_values --bottleneck 
            AND s.subscriber_type =sub_type.param_values
            and sg.agreement_no=chg.agreement_no
            and sg.soc=chg.soc
            and sg.soc_seq_no=chg.soc_seq_no
            and chg.expiration_date is null
            and cp.pym_channel_no=chg.target_pcn
            and exists(
            select 1 from ar1_payment pym,hr_pay_trx            
            where cp.ban = pym.account_id
           AND (pym.transaction_id > hr_pay_trx.param_values
                AND
               pym.transaction_id

可以看到from后面跟了好几个大表,但是性能瓶颈在service_details上所以为了保险起见,我们可以使用hint来指定表的访问顺序。先过滤到99%以上的数据,剩下的就可以自然的走索引扫描了。
添加的Hint如下,对于表service_details,因为没法使用到索引,所以就对这一个表进行全表扫描,走个并行。
SELECT /*+leading(ssrc,sub_type,sg,s,chg,cp) parallel(sg 4) full(sg)*/
           cp.pym_channel_no,sg.agreement_no, s.subscriber_no, s.prim_resource_val

最后在备份库中测试,效果果然很明显,备份库中速度从优化后的4分钟降低到2分钟。
在生产环境中执行,速度更快,稳定在40秒左右。

时间: 2024-10-26 02:21:14

生产环境sql语句调优实战第八篇的相关文章

生产环境sql语句调优实战第十篇

陆陆续续写了九篇关于生产环境sql语句的调优案例,发现了不少问题,可能有些问题回头来看是比较低级的错误,稍加改动就能够运行在秒级,有些可能是在秒级到毫秒级的小步提升等等,不管调优的改进多大,从dba的角度来看,好多问题都是基于资源来调优的,比如添加索引,降低IO,降低CPU消耗,提高CPU利用率等等.如果有时候从业务角度来下下功夫,可能某种程度上效果要更好于基于资源/代价的调优. 最近客户反馈有几条sql语句IO消耗很高,希望我们能够给提点建议. sql语句很短,但是运行时间在9秒左右.运行频率

生产环境sql语句调优实战第六篇

生产环境中有大量的sql语句在运行,尽管有awr,ash做数据的收集统计,但是dba的调优工作大多数情况都是在问题已经发生后做排查的,有些sql语句可能执行的时间有1,2分钟左右,但是sql语句本身有潜在的性能问题,通过awr是定位不到的,ash尽管能够查到,但是我们在未知的情况下怎么知道问题发生的精确时间点,通过sql monitor能够查到一些实时的性能问题,但是还是需要按照自己的情况和要求来不间断地进行性能的监控.通过一个工具一劳永逸是不现实的. 今天想做数据迁移也有些日子了,看看生产环境

生产环境sql语句调优实战第七篇

在数据迁移完成之后,开始了例行的后期数据库维护,早上一来就发现了一个sql执行时间很长了.达到了37279秒.最后在改进调优之后执行速度在1分钟以内. 这个速度是毫无疑问的性能问题,但是是否是因为数据迁移直接导致的呢,通过简单的脚本分析,得出了如下的图表. 显示了同样的sql语句在7月份至今的执行情况,还真是奇怪,昨天以前一直正常,期间也进行过两次数据迁移,昨天的数据迁移完成以后,性能就出奇的差. 通过sql_monitor得到了执行计划和对应的sql语句. 可以看到执行时间是昨天的下午,一直执

生产环境sql语句调优实战第三篇

生产环境有一条sql语句执行比较频繁,占用了大量的cpu资源.原本执行需要花费11秒.在一次排查中引起了我的注意,决定看看cpu消耗到底在哪儿? sql语句是比较简单的,通过查询SUBSCRIBER_FA_V是一个视图.在视图中关联了几个和业务核心表. SELECT TO_CHAR(SUBSCRIBER_NO) SUBSCRIBER_ID,        SUB_STATUS,        SUB_STS_RSN_CD,        TO_CHAR(SUB_STATUS_DATE, 'yyy

生产环境sql语句调优实战第四篇

生产中有一条sql语句消耗了大量的cpu资源,执行时间在18秒左右, Session : PRODBUSER (1560:61133) SQL ID : 1hg2wcuapy3y3 SQL Execution ID : 16871963 Execution Started : 07/21/2014 12:30:20 First Refresh Time : 07/21/2014 12:30:24 Last Refresh Time : 07/21/2014 12:30:37 Duration :

生产环境sql语句调优实战第九篇

生产环境中有一些sql语句是不定时炸弹,不声不响的运行着,可能相关的表很大,运行时间达数小时甚至数天. 上周在生产环境中发现一条sql语句,运行时间几乎是按照天来计算的.看到这种sql语句,就希望能够马上能够找到立竿见影的效果,不过欲速则不达,需要做不少工作.一定要反复验证可行. sql语句的运行情况如下: SQL Execution ID : 16777217  Execution Started : 10/18/2014 11:46:30  First Refresh Time : 10/1

生产环境sql语句调优实战第二篇

在生产环境通过sql monitor监控到有一条sql执行效率很差.执行了大约5个小时,得到的sql monitor报告如下: Global Information: EXECUTING Instance ID : 1 Buffer Gets IO Requests Database Time Wait Activity . 40M . 17M . . 8450s . . . 100% Session : PRODUSER(14:22343) SQL ID : fkzafpjs28d6d SQL

生产环境大型sql语句调优实战第一篇(一)

在生产环境中有一条sql语句的性能极差,在早晨非高峰时段运行抽取数据,平均要花费40分钟,有时候竟然要跑10个多小时.sql语句比较长,需要点耐心往下看.我对表的数据量都做了简单的说明. 首先拿到sql语句,一看到关联的表有一长串,而且都是很大的表.如果性能很差,可能和走全表扫描有关,还有可能和多表关联时,表的查取顺序也有一定的关系. SELECT   DISTINCT CA.L9_CONVERGENCE_CODE AS ATB2,                 CU.CUST_SUB_TYP

生产环境大型sql语句调优实战第一篇(二)

继续昨天的部分,上一篇的链接为: http://blog.itpub.net/23718752/viewspace-1217012/ 对这条大sql的性能瓶颈进行了分析.主要瓶颈在于一个很大的业务表,数据量在亿级.如果通过时间条件来过滤,会有5%以内的数据被过滤出来. 但是没有时间相关的索引字段,所以会走全表扫描,在目前的产品线中,这个大分区表的索引时严格控制的,所以最后经过测试和比对,还是考虑加并行来提高数据的查取速度. --查找性能瓶颈,根据反馈,查取的数据其实并不错,可能在几千条以内的样子