现在的位置: 首页 > 综合 > 正文

同样的sql传入的条件不同执行效率相差百倍的原因

2014年01月18日 ⁄ 综合 ⁄ 共 2906字 ⁄ 字号 评论关闭

这个是公司另一个项目组遇到的事情,记录下

**项目有一条查询SQL是这样的:

select s.id                id ,

      s.end_user_id       endUserId,

      s.order_code        orderCode,

      s.order_create_time orderCreateTime,

      s.receive_date      receiveDate,

      s.is_leaf           isLeaf,

      s.parent_so_id      parentSoId,

      s.site_type         siteType,

      s.merchant_id       merchantId,

      s.order_Type        orderType

  from ( select rownum rn,
rd

          from (select rowid rd

                  from so_data2.so si

                 where si.receive_date>= to_date(#receiveDate# 'yyyy-mm-dd' )

                   and si.receive_date< to_date(#receiveDate#  'yyyy-mm-dd' )

                   and si.is_leaf= 1

                   and si.order_status in ( 2435 )

                   and si.update_time>= to_date(#receiveDate#  'yyyy-mm-dd' )

                   and si.order_create_time>=

                      to_date( '2013-04-01 00:00:00' , 'yyyy-mm-ddhh24:mi:ss')

                   and si.order_create_time<

                      to_date(#receiveDate#  , 'yyyy-mm-dd' ))

         where rownum <= 1000)
p,

      so_data2.so s

  where p.rn> 0

   and p.rd= s.rowid

 

这条SQL,只有收货时间是入参。并且有3个条件:

si.receive_date>= to_date(#receiveDate# 'yyyy-mm-dd' )

and si.receive_date< to_date(#receiveDate#  'yyyy-mm-dd' )

and si.update_time>= to_date(#receiveDate#  'yyyy-mm-dd' )

and si.order_create_time< to_date(#receiveDate#  , 'yyyy-mm-dd' ))

其中receive_date是>= 和 < 2个条件,update_time只是>=这1个条件。

 

问题描述:

在开始初始化**数据的时候,是从2013-04-01开始查数据的,每次查一天。那时候初始化数据,这个SQL执行效率大概在10S以内;

初始化数据结束之后,定时任务每天晚上查询前一天收货完成的订单,效率也都是在10S以内。

 

这次**体系的开发,也需要用这个接口来初始数据,从2013-07-01开始查询收货完成的订单。但是上STG就发现,这个接口非常慢,需要将近2、3分钟,有时候还会超时。但是就是在会员体系调这个接口超时的情况下,凌晨查询前一天订单的定时任务调这个接口,还是稳定在10s左右。同一个SQL,只是参数不同,为什么差别会这么大呢?并且之前**初始化数据的时候,也是查几个月前的数据,为什么那时候初始化效率也很高呢?

 

定位过程:

Step1:首先怀疑BI数据库是否做了历史数据归档等操作,才导致查前一天的很快,但是查7月份数据很慢。通过咨询BI同事,他们数据库并没有做什么归档,都是按照时间分区的,并没有什么变化。

 

Step2:把SQL发给BI同事,他们在PL/SQL客户端执行这条sql,时间就填成7月的某一天,也是在几秒内就返回了,并不会存在效率问题。

最后,找到DBA**,让我们在SQL里加上/*+ louis */,加完之后效果马上显现了,这个SQL又从2分多钟,恢复到了几秒。鉴证奇迹的时候到了。当时都不明白这是什么情况,把代码回滚又测了一次,结果执行时间还是2分多钟,再把这个神奇的/*+
louis */加上,立马又恢复到了几秒以内。

 

Step3:咨询了下DBA,这个/*+ louis */是什么意思,刚开始,他让我们加这个的意思是加个标记,到时候好分析这个SQL执行时的执行计划,没想到加了之后,问题立马就解决了。百度一下,才知道这个是orcale的机制--HINT。之所以加了/*+ louis */效率会提升,是因为指定的这种机制louis(其实,这个是DBA的英文名),不存在,所以查询优化器会重新解析这条SQL,重新根据绑定的参数制定执行计划,所以又可以用到receive_date字段的索引了。

 

最后,百度了一下,大概得出了如下的分析结论:

1、查4月份数据,数据查询优化器会走receive_date这个索引,因为update_time这个数据非常大。所以,当时初始化数据的时候,效率也是很高,查询SQL大概在秒级;

2、现在初始化数据跑完了,每天查前一天的数据,这个时候,数据库自动优化成了走update_time的索引。

3、当这时候再次执行这条SQL,传的日期是7月1号,由于是通过绑定变量的方式,执行计划没有改变,还是走的update_time这个索引,而日期是7月1号,并且条件里只有update_time >= ,没有小于,这时候用update_time的索引,效率就非常低了。

4、采用hint方式,指定走receive_date索引,在SQL中第一行改成:select /*+ INDEX(SIIDX_YHD_SO_RECEIVE_DATE) */ s.id id, 

 ,传入7月1号,执行时间从之前的200S+,降低到了2S左右,效率提升了100+倍了

 

结合之前fraudpool有条SQL,在PL/SQL执行和代码调用耗时查表非常大,也是因为是代码通过绑定变量的方式,导致数据库采用了之前解析过的执行计划,从而没有走到理想的索引。类似这里问题,以后大家都可以通过HINT这种方式尝尝试优化一下,效果,呵呵,谁用谁知道。

 

关于HINT的用法和疗效,大家可以自己百度一下,这里就不贴文档了。如果有兴趣,可以去了解一下,相信有帮助。

抱歉!评论已关闭.