分页SQL优化之一

简单优化。SQL_ID:ads09ymdgr597 业务高峰期  21万逻辑读/次   业务高峰期逻辑读TOP1
该次优化发现在数据库中一个SQL多个执行计划,是否有特别低效的拉大平均值?  单次21万其实不算太多。
select * from table(dbms_xplan.display('ads09ymdgr597',null)); 就一个执行计划。 那证明就搞这个得了。
话不多说:SQL(带入绑定变量后的SQL)

   SELECT *
   FROM (SELECT XX.*, ROWNUM AS RN
           FROM (select o.OFFER_ID,
                        o.OFFER_NAME,
                        o.OFFER_TYPE,
                        o.OFFER_NBR,
                        o.OFFER_SYS_TYPE,
                        o.OFFER_SYS_NAME,
                        o.OFFER_SYS_PY_NAME,
                        o.OFFER_DESC,
                        o.EFF_DATE,
                        o.MANAGE_GRADE,
                        o.EXP_DATE,
                        o.OFFER_PROVIDER_ID,
                        o.BRAND_ID,
                        o.VALUE_ADDED_FLAG,
                        o.INITIAL_CRED_VALUE,
                        o.PRICING_PLAN_ID,
                        o.IS_INDEPENDENT,
                        o.MANAGE_REGION_ID,
                        cr.REGION_NAME,
                        o.STATUS_CD,
                        a.ORDER_TIMES_RULE_ID as A_ORDER_TIMES_RULE_ID,
                        a.OFFER_ID            as A_OFFER_ID,
                        a.OBJ_TYPE            as A_OBJ_TYPE,
                        a.ORDER_TIMES         as A_ORDER_TIMES,
                        a.TIME_TYPE           as A_TIME_TYPE,
                        a.LIMIT_TIME          as A_LIMIT_TIME,
                        a.TIME_UNIT           as A_TIME_UNIT,
                        a.EFF_DATE            as A_EFF_DATE,
                        a.EXP_DATE            as A_EXP_DATE,
                        a.ORDER_TYPE          as A_ORDER_TYPE,
                        a.APPLY_REGION_ID     as A_APPLY_REGION_ID,
                        a.STATUS_CD           as A_STATUS_CD,
                        a.CREATE_STAFF        as A_CREATE_STAFF,
                        a.UPDATE_STAFF        as A_UPDATE_STAFF,
                        a.STATUS_DATE         as A_STATUS_DATE,
                        a.CREATE_DATE         as A_CREATE_DATE,
                        a.UPDATE_DATE         as A_UPDATE_DATE,
                        a.REMARK              as A_REMARK,
                        a.OFFER_PROD_REL_ID   as A_OFFER_PROD_REL_ID
                   from SPEC_APP_HA.OFFER o
                   left join SPEC_APP_HA.COMMON_REGION cr
                     on cr.COMMON_REGION_ID = o.MANAGE_REGION_ID
                   left join SPEC_APP_HA.OFFER_ORDER_TIMES_RULE a
                     on a.offer_id = o.offer_id
                  where not exists (select 1
                           from SPEC_APP_HA.offer_ext_attr ext
                          where ext.attr_id = '300000007'
                            and ext.offer_id = o.offer_id
                            and ext.STATUS_CD = '1000')
                    and not exists
                  (select 1
                           from SPEC_APP_HA.PRODUCT p
                          where p.BASE_OFFER_ID = o.OFFER_ID)
                    and o.OFFER_TYPE != '10'
                    and o.OFFER_ID in
                        (select  distinct c.offer_id
                           from SPEC_APP_HA.offer_grp_member o
                           left join SPEC_APP_HA.offer_prod_rel a
                             on a.offer_id = o.offer_id
                           left join SPEC_APP_HA.product b
                             on b.prod_id = a.prod_id
                           left join SPEC_APP_HA.offer c
                             on c.offer_id = o.offer_id
                          where o.offer_grp_id in
                                (select z_offer_grp_id
                                   from SPEC_APP_HA.offer_rel
                                  where a_offer_grp_id in
                                        (select distinct offer_grp_id
                                           from SPEC_APP_HA.offer_grp_member
                                          where obj_type = '110000'
                                            and status_cd = '1000'
                                            and offer_id in (300500003485, 300500003485, 911008800, 911008801)
                                            and APPLY_REGION_ID in
                                                (8320826, 8320800, 8320000, -9999))
                                    and rel_type = '700000'
                                    and status_cd = '1000'
                                    and APPLY_REGION_ID in (8320826, 8320800, 8320000, -9999))
                            and (b.prod_id in
                                (select x.z_prod_id
                                    from SPEC_APP_HA.prod_rel x
                                   where x.rel_type = '100600'
                                     and x.a_prod_id = 100000379
                                     and x.status_cd = '1000'
                                     and x.APPLY_REGION_ID in
                                         (8320826, 8320800, 8320000, -9999)) 
                                         or
                                b.prod_id in
                                (select pcpr.prod_id
                                    from SPEC_APP_HA.prod_comp_prod_rel pcpr
                                    left join SPEC_APP_HA.prod_rel pr
                                      on pr.prod_rel_id = pcpr.prod_rel_id
                                   where pcpr.rel_type != '1400'
                                     and pr.z_prod_id = 100000379
                                     and pr.rel_type = '100500'
                                     and pr.status_cd = '1000'
                                     and pcpr.status_cd = '1000'
                                     and pr.APPLY_REGION_ID in
                                         (8320826, 8320800, 8320000, -9999)) 
                                 or  b.prod_id = 100000379)
                            and c.EFF_DATE <= sysdate
                            and c.EXP_DATE >= sysdate
                            and c.status_cd = '1000'
                            and b.status_cd = '1000'
                            and a.status_cd = '1000'
                            and o.status_cd = '1000'
                            and o.APPLY_REGION_ID in (8320826, 8320800, 8320000, -9999))
                    and o.OFFER_TYPE = 13
                    and o.MANAGE_REGION_ID in (8320826, 8320800, 8320000, -9999)) XX
          WHERE ROWNUM <= 10) XXX
  WHERE RN > 0;

一开始看还真没有看到可疑的地方。 
于是看执行计划。


看到可疑的地方,当时找的可疑的地方。
|* 18 |                   TABLE ACCESS BY INDEX ROWID| OFFER_REL              |     1 |    21 |     2   (0)| 00:00:01 |
|* 19 |                    INDEX FULL SCAN           | IDX_OFFER_REL_1        |  

|* 29 |            TABLE ACCESS FULL                 | PROD_REL  

|* 32 |              TABLE ACCESS FULL               | PROD_COMP_PROD_REL     |  

|* 43 |     TABLE ACCESS FULL                        | PRODUCT                |

|* 10 |           FILTER                             |                        |       |       |            |          |
|* 11 |            FILTER      

看下表大小。最大的表 OFFER_GRP_MEMBER 16M。  OFFER 8M   反正都是小表。这个不说了。
有些人说怎么多表看起来麻烦, 优化时候估计会偷懒, 其实有简单方法。  我都是用这个方法的

select segment_name, max(se.owner) owner, sum(bytes/1024/1024) tab_SIZE_MB, decode(max(partition_name),null,'NO','YES')  tabpartitioned
       from  dba_segments se 
      where   se.segment_type like 'TABLE%'   
    and (se.segment_name,se.owner)  in  ( select t.object_name,t.object_owner  from  v$sql_plan t where sql_id = 'ads09ymdgr597' )  
      group  by  se.segment_name ,se.owner;

当然 基于这个方法可以去关联其他信息。  

然后调查可疑地方的问题, 评估该步是否该走索引, 该走全表....  

哥懒得一步步搞了。 毕竟SQl太复杂了。直接 A-rows E-rows 的值计划。

谓词信息不贴了和上面一样。

是不是这一步成本最高的??
* 28 |            TABLE ACCESS FULL                | PROD_REL               |    695 |      1 |    197 |00:00:00.48 |     104K| 

有人说全表扫描莫, 在该步弄一个index。  这样其实不好。弄清楚逻辑再说。对应id = 10 filter。 疑问 一般要么nested_loop  要莫hash. 这步为啥filter?
怎么还有filter??  在SQL中寻找答案。 对应的SQl条件   and (b.prod_id in  (  )    or  b.prod_id in   ( )    or  b.prod_id = 100000379)。 这??  这!这 不无语。难怪会filter。

哥干脆把这个条件去掉看看效率再说。

Predicate Information (identified by operation id):
 --- 谓词信息就不贴了。。
去掉  and (b.prod_id in  (  )    or  b.prod_id in   ( )    or  b.prod_id = 100000379) 后逻辑读减少到9万。

 大头继续优化。

还行基本上是预期了。 在执行计划中, 大头在Id = 20,21,22,23 中。这边一眼看到性能瓶颈。 nested_loop 性能瓶颈之一 于是换成hash 试试再说。 哥把性能大头地方对应的SQL剥离开来,得到SQL:

select    distinct c.offer_id      from SPEC_APP_HA.offer_grp_member o
                           left join SPEC_APP_HA.offer_prod_rel a  on a.offer_id = o.offer_id
                           left join SPEC_APP_HA.product b    on b.prod_id = a.prod_id
                           left join SPEC_APP_HA.offer c   on c.offer_id = o.offer_id
                          where o.offer_grp_id in
                                (select z_offer_grp_id   from SPEC_APP_HA.offer_rel
                                  where a_offer_grp_id in
                                        (select distinct offer_grp_id   from SPEC_APP_HA.offer_grp_member
                                          where obj_type = '110000'  and status_cd = '1000'
                                            and offer_id in (300500003485, 300500003485, 911008800, 911008801)
                                            and APPLY_REGION_ID in      (8320826, 8320800, 8320000, -9999))
                                    and rel_type = '700000'   and status_cd = '1000'
                      and APPLY_REGION_ID in (8320826, 8320800, 8320000, -9999)) 
                       and c.EFF_DATE <= sysdate  and c.EXP_DATE >= sysdate
                      and c.status_cd = '1000'    and b.status_cd = '1000'  and a.status_cd = '1000'     and o.status_cd = '1000'
                   and o.APPLY_REGION_ID in (8320826, 8320800, 8320000, -9999);

优化这个SQL,由于篇幅问题,而且执行计划和上面对应的地方的执行计划一样的。

 分两次执行 第一次原版执行, 第二次添加hints /*+ use_hash(o,c)  use_hash(o,a)  use_hash(b,a)  */  

 原版: 
 统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
      89483  consistent gets
          0  physical reads
          0  redo size
      .....
       1501  rows processed

看到8.9万逻辑读我当时是非常高兴的,  不知道有没有读者注意到上面说的一句:
and (b.prod_id in  (  )    or  b.prod_id in   ( )    or  b.prod_id = 100000379) 后逻辑读减少到9万。
那证明再一次找到性能瓶颈的大头。这个如果能优化。那肯定效果很不多啊。

 添加hints后:
 统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       3669  consistent gets
          0  physical reads
          0  redo size
      ....
          0  sorts (disk)
       1501  rows processed

这个小SQL优化好了后融入大SQL中, 逻辑读只有4000左右,得出结论这边走hash 性能最佳。 这一步优化好了再 添加第一次去掉的 in or in 部分

and (b.prod_id in  (  )    or  b.prod_id in   ( )    or  b.prod_id = 100000379) 这种SQl的改写方案
改成 and (b.prod_id in  (  union all )): 

 SQL:SQL 就不贴全了,太长了。
  SELECT *
   FROM (SELECT XX.*, ROWNUM AS RN
           FROM (select o.OFFER_ID, ....
                        a.OFFER_PROD_REL_ID   as A_OFFER_PROD_REL_ID
                   from SPEC_APP_HA.OFFER o
                   left join SPEC_APP_HA.COMMON_REGION cr
                     on cr.COMMON_REGION_ID = o.MANAGE_REGION_ID
                   left join SPEC_APP_HA.OFFER_ORDER_TIMES_RULE a
                     on a.offer_id = o.offer_id
                  where ....
                    and o.OFFER_ID in
                        (select  /*+ use_hash(o,c)  use_hash(o,a)  use_hash(b,a)  */   c.offer_id ...  )
                       and b.prod_id in
                                (select  x.z_prod_id  from SPEC_APP_HA.prod_rel x ...
                                    union all
                                   select pcpr.prod_id   from SPEC_APP_HA.prod_comp_prod_rel pcpr
                                    union all
                                     select 100000379 from dual    

                                    )  
                         ... ) XX
          WHERE ROWNUM <= 10) XXX
  WHERE RN > 0;

 还是6.8万逻辑读。。。。
这边就不说了(写两句吃饭去了),  如果你不能一眼看出性能瓶颈 那你继续修炼吧。

于是添加hint

  SELECT *
   FROM (SELECT XX.*, ROWNUM AS RN
           FROM (select o.OFFER_ID, ....
                        a.OFFER_PROD_REL_ID   as A_OFFER_PROD_REL_ID
                   from SPEC_APP_HA.OFFER o
                   left join SPEC_APP_HA.COMMON_REGION cr
                     on cr.COMMON_REGION_ID = o.MANAGE_REGION_ID
                   left join SPEC_APP_HA.OFFER_ORDER_TIMES_RULE a
                     on a.offer_id = o.offer_id
                  where ....
                    and o.OFFER_ID in
                        (select  /*+ use_hash(o,c)  use_hash(o,a)  use_hash(b,a)  */   c.offer_id ...  )
                       and b.prod_id in
                                (select /*+ unnest */    x.z_prod_id  from SPEC_APP_HA.prod_rel x ...
                                    union all
                                   select pcpr.prod_id   from SPEC_APP_HA.prod_comp_prod_rel pcpr
                                    union all
                                     select 100000379 from dual    
                                    )  
                         ... ) XX
          WHERE ROWNUM <= 10) XXX
  WHERE RN > 0;
执行:

 总结: 21万逻辑读。 优化到 6万,再优化到5千逻辑读。 

还有可以优化的空间,读者朋友有兴趣的可以继续看看。

SQL:

SELECT *
   FROM (SELECT XX.*, ROWNUM AS RN
           FROM (select o.OFFER_ID, ........  a.OFFER_PROD_REL_ID   as A_OFFER_PROD_REL_ID
                   from SPEC_APP_HA.OFFER o
                   left join SPEC_APP_HA.COMMON_REGION cr   on cr.COMMON_REGION_ID = o.MANAGE_REGION_ID
                   left join SPEC_APP_HA.OFFER_ORDER_TIMES_RULE a    on a.offer_id = o.offer_id
                  where not exists (select 1   from SPEC_APP_HA.offer_ext_attr ext
                          where ext.attr_id = '300000007'       and ext.offer_id = o.offer_id     and ext.STATUS_CD = '1000')
                    and not exists
                  (select 1     from SPEC_APP_HA.PRODUCT p
                          where p.BASE_OFFER_ID = o.OFFER_ID)
                    and o.OFFER_TYPE != '10'
                    and o.OFFER_ID in
                        (select  /*+ use_hash(o,c)  use_hash(o,a)  use_hash(b,a)  */   c.offer_id
                           from SPEC_APP_HA.offer_grp_member o
                           left join SPEC_APP_HA.offer_prod_rel a
                             on a.offer_id = o.offer_id
                           left join SPEC_APP_HA.product b
                             on b.prod_id = a.prod_id
                           left join SPEC_APP_HA.offer c
                             on c.offer_id = o.offer_id
                          where o.offer_grp_id in
                                (select z_offer_grp_id
                                   from SPEC_APP_HA.offer_rel
                                  where a_offer_grp_id in
                                        (select distinct offer_grp_id
                                           from SPEC_APP_HA.offer_grp_member
                                          where obj_type = '110000'
                                            and status_cd = '1000'
                                            and offer_id in (300500003485, 300500003485, 911008800, 911008801)
                                            and APPLY_REGION_ID in
                                                (8320826, 8320800, 8320000, -9999))
                                    and rel_type = '700000'
                                    and status_cd = '1000'
                                    and APPLY_REGION_ID in (8320826, 8320800, 8320000, -9999))
                               and b.prod_id in
                                (select   /*+ unnest */   x.z_prod_id
                                    from SPEC_APP_HA.prod_rel x
                                   where x.rel_type = '100600'
                                     and x.a_prod_id = 100000379
                                     and x.status_cd = '1000'
                                     and x.APPLY_REGION_ID in  (8320826, 8320800, 8320000, -9999) 
                                    union all
                                   select pcpr.prod_id
                                    from SPEC_APP_HA.prod_comp_prod_rel pcpr
                                    left join SPEC_APP_HA.prod_rel pr
                                      on pr.prod_rel_id = pcpr.prod_rel_id
                                   where pcpr.rel_type != '1400'
                                     and pr.z_prod_id = 100000379
                                     and pr.rel_type = '100500'
                                     and pr.status_cd = '1000'
                                     and pcpr.status_cd = '1000'
                                     and pr.APPLY_REGION_ID in
                                         (8320826, 8320800, 8320000, -9999) 
                                     union all
                                     select 100000379 from dual    
                                    )  
                            and c.EFF_DATE <= sysdate
                            and c.EXP_DATE >= sysdate
                            and c.status_cd = '1000'
                            and b.status_cd = '1000'
                            and a.status_cd = '1000'
                            and o.status_cd = '1000'
                            and o.APPLY_REGION_ID in (8320826, 8320800, 8320000, -9999))
                    and o.OFFER_TYPE = 13
                    and o.MANAGE_REGION_ID in (8320826, 8320800, 8320000, -9999)) XX
          WHERE ROWNUM <= 10) XXX
  WHERE RN > 0;

猜你喜欢

转载自blog.csdn.net/daiqiulong2/article/details/84102515