白鳝的洞穴 ( 白鳝与Oracle的亲密接触 ) 给他(她)留言   |  相册  |  回到专栏  |  管理  |   登录  博客首页
白鳝在工作中的点滴积累,不仅仅包括技术的
白鳝
  •    我的栏目
  •   我的文章
      Oracle杂谈
      内部分析
      优化
      案例
      小技巧
      BUG与故障
      SQL与PL/SQL开发
      DBA日记
      IT长篇小说第一部:IT的I
  •    最新文章
  •   DBA日记第三部 像Oracle一样
      DBA日记第三部 像Oracle一样
      DBA日记第三部 像Oracle一样
      DBA日记第三部 像Oracle一样
      DBA日记第三部 像Oracle一样
      CPU_COUNT对共享池的影响
      DBA日记 第三部 像Oracle一样
      DBA日记 第三部 像Oracle一样
      3月24日 简单任务 (1)令人
      3月23日 理解表的存储结构 (
  •    最新评论
  •   [HadaVopsesoto]   Would you like to play solitaire against real persons?
      [飞帆]   回复:DBA日记第三部 像Oracle一样思考 3月28日 理解索引(1)
      [xhh]   回复:健康性检查
      [jimlist]   回复:Oracle常用EVENT参考(3)
      [edwards6309]   回复:DBA日记第三部 像Oracle一样思考 4月2日 索引危机(3)效果不错
      [eagle_fan]   回复:DBA日记第三部 像Oracle一样思考 4月2日 索引危机(3)效果不错
      [白鳝]   回复:DBA日记第三部 像Oracle一样思考 4月2日 索引危机(3)效果不错
      [白鳝]   回复:DBA日记第三部 像Oracle一样思考 4月2日 索引危机(3)效果不错
      [killkill]   Re:DBA日记第三部 像Oracle一样思考 4月2日 索引危机(3)效果不错
      [sir.liang]   回复:健康性检查
  •    博客统计
  •   文章 - 166
      评论 -772
      访问 - 57818
  •    友情链接
  • 主题:DBA日记第三部 像Oracle一样思考 4月2日 索引危机(2)索引范围扫描的优化方法 发表时间:2010-6-7 14:52:37 
    作者:白鳝  离线 回复:9   浏览:1386

    42日 索引危机(2)索引范围扫描的优化方法

    昨天下午又优化了几个SQL,对于这几个SQL来说,优化后都有了明显的性能提升,不过从今天上午10点多观察到的CPU使用率来看,好像变化不大。由于排队效应的存在,简单的减法并不能完全解决问题。以前排在前面的几个SQL解决了以后,又冒出了几条新的SQL,其中一条SQL每次执行的BUFFER GET数量高达3万多:

    select a.filename, b.source_id, b.source_path, b.localnet_abbr, b.file_fmt,b.is_txt_fmt, a.deal_flag       

    from SCH_XXX a, BBB b 

    where (a.deal_flag='W' or a.deal_flag='B') and

     a.validflag='Y' and b.pipe_id=:szPipeId and a.source_id=b.source_id

    从执行计划上看:

    --------------------------------------------------------------------------------

    | Operation                      | PHV/Object Name     |  Rows | Bytes|   Cost |

    --------------------------------------------------------------------------------

    |SELECT STATEMENT                |----- 1104374539 ----|       |      |   3486 |

    |HASH JOIN                       |                     |    79K|    7M|   3486 |

    | TABLE ACCESS BY INDEX ROWID    |BBBBB                |     2 |   98 |      2 |

    |  INDEX RANGE SCAN              |FK_SOURCE_WORKFLOW   |     1 |      |      1 |

    | TABLE ACCESS FULL              |SCH_XXXXX             |     1M|   61M|   3474 |

    --------------------------------------------------------------------------------

    从这个执行计划上看,对SCH_XXXX的扫描是开销最大的,这张表中扫描的记录数位100万(1M)。通过SPREPSQL报告,我们看到:

                                                           

                         Statement Total      Per Execute  

                         ---------------  ---------------  

            Buffer Gets:         626,984         39,186.5  

             Disk Reads:         425,693         26,605.8  

         Rows processed:              46              2.9

         CPU Time(s/ms):              50          3,113.1

     Elapsed Time(s/ms):             988         61,779.7

                  Sorts:               0               .0

            Parse Calls:              16              1.0

          Invalidations:               0

          Version count:               2

        Sharable Mem(K):              29

             Executions:              16

    平均每次执行返回的记录数是2.9条,也就是说真正符合条件的记录只有区区3条左右。这和执行计划中的79K相差甚远,于是我检查了一下这张表,这张表总计有200万条记录。在这张表上,有两个过滤条件(a.deal_flag='W' or a.deal_flag='B') and  a.validflag='Y'。我首先分析了一下validflag

    Select validflag,count(*) from sch_xxxx group by validflag;

    发现表中的大多数记录都是validflag='Y'的记录,这个字段的选择性是很差的,于是我又检查了一下deal_flag,发现绝大多数记录都是deal_flag='Y'的,其他的记录十分少,有时候甚至是0。这是一个典型的字段倾斜的案例,凑巧的是在这个SQL种并没有使用绑定变量,于是在deal_flag上创建一个索引,并且分析一下柱状图就可以解决问题。可客户协商了一下,这张表只有200万记录,可以立即创建索引,并做表分析,于是我马上执行了下面的脚本:

    CREATE INDEX SHCJ.IDX_DEALFLAG ON SHCJ.SCH_XXXX(DEAL_FLAG) ;

    Exec DBMS_STATS.GATHER_TABLE_STATS(ownname=> 'SHCJ', tabname=> 'SCH_XXXX', estimate_percent=>30, method_opt=>'for all indexed columns size skewonly',cascade=>true, degree=>2);

    索引创建后,再次分析了表和索引,其中表中对索引字段中倾斜的字段分析了柱状图。Method_opt也可以设为'for all columns size auto',这是10G的缺省,而9i的缺省是'for all columns size 1',也就是不分析柱状图。

    上诉操作完成后,我马上最这个SQL进行了分析,发现BUFFER GET的数量从近4万减少到了10.7,这个SQL产生的物理读基本上没有了。这说明和我判断的一致,这个索引起到了很好的作用。

    从上午业务高峰期的情况来看,虽然又调整了3/4SQL,不过总体的效果并没有很明显的显现。上午10-1130之间,系统CPU使用率仍然在95-100%高位,平均事务响应时间虽然有了20%左右的提升,但是感觉还是很难彻底解决问题。在STATSPACK报告中,已经找不到明显的可以通过索引调整进行优化的SQL了,如何解决CPU负荷过高的问题,好像还是没有半点眉目。这套系统的SQL开销比较平均,除了排在BUFFER GET第一位的那条SQL占了整个开销的37%外,其他的SQL占整个系统BUFFER GET的比例都不高,最多的也只是在4%左右。调整这些SQL,虽然能够取得一些效果,不过效果还是不明显。看样子如果想要走捷径,必须优化这个排名TOP 1SQL。于是我再次看了看这个SQL,这条SQL涉及到两张表,一张稍微大点的表T_PRODUCT_INFO,这张表的WHERE条件中有一个PRODUCT_TYPE=:P1 AND PRODUCT_CLASS=:P2的过滤条件,另外一张表是涨小表,大概只有20来条记录。执行计划是扫描完两张表后,结果集做HASH JOIN。在T_PRODUCT_INFO表,有一个复合索引(PRODUCT_TYPE,PRODUCT_CLASS),执行计划也是通过对这个索引做范围扫描的。从执行计划上来看,是没有问题的。我看了一下,平均每次返回结果的记录数不过60多条。而T_PRODUCT_INFO做索引范围扫描找到的记录是接近700条。T_PRODUCT_INFO表的记录数为800万,从一个800万记录的表中,扫描出700条记录,这个索引的效率还是比较高的。为什么这么一个简单的SQL会产生1500多个BUFFER GET呢?我马上联想到了CLUSTER FACTOR,于是马上查了DBA_INDEXES,果然,这个索引的CLUSTER FACTOR接近记录的数量。CLUSTER FACTOR较高,在通过索引范围扫描访问表的时候,由于每个索引键值更多的指向新数据块,因此会产生更多的物理IO,并导致BUFFER GET的数量增加。

    我马上找到郭工,了解这张表的用途。经过了解,这张表是存放产品的资料的,在做售后服务的时候,会通过PRODUCT_TYPEPRODUCT_CLASS,找到符合条件的备件,并根据库存情况以及本次服务的其他限定条件,查出满足条件的备件。这张表的数据是逐步添加的,随着新的产品及其备件的出现,这张表的数据也越来越多。以前这张表只有1/200万条记录,随着这几年的积累,记录数已经接近100万了。我想了一下,这张表的记录的加入,肯定不会按照PRODUCT_TYPEPRODUCT_CLASS进行排序,因此这个索引的CLUSTER FACTOR比较大也就不足为奇了。于是我问郭工,对于这张表的访问,是不是大多数都是通过PRODUCT_TYPEPRODUCT_CLASS进行,郭工想了想,说:“是的,绝大多数访问这张表都是根据这两个字段的条件,另外一种就是根据PRODUCT_ID直接定位产品。”PRODUCT_ID是这张表的主键,既然这张表主要的访问方式只有这两种,那么我倒是想出了一个优化方案。

    由于这张表记录的插入顺序没有按照PRODUCT_TYPEPRODUCT_CLASS排序,所以索引范围扫描的成本较高。如果我们能够对这张表进行重组,使之按照这两个字段排序,那么范围扫描的成本就可以得到有效的减少。采用如下的方法可以完成这个优化:

    1、将原表renamet_product_info_old

    2、Create table t_product_info as select *  from t_product_info old order by product_type,product_class

    3、创建相关索引及约束关系

    4、分析表和索引

    我和郭工商量了一下能否今晚实施一下这个优化操作。郭工听我说了半天CLUSTER FACTOR,也没听明白。他说:“老白,技术的问题我就不管了,我帮你申请一下今晚停机的事情。这套系统晚上就只有一些统计报表,所以晚上19点到21点之间都是可以停库的。如果你的操作能够在2个小时内完成,停机申请就没任何问题,否则我就需要和业务部门协商了。我想了想,这张表也就几百M的大小,1个小时肯定能解决问题了。

    本文链接:http://www.oraclefans.cn/blog/showblog.jsp?rootid=19756
     
           网友评论
    ─ 评论人 obuntu    10-06-08 07:43
      这是一个典型的字段倾斜的案例,凑巧的是在这个SQL种并没有使用绑定变量,于是在deal_flag上创建一个索引,并且分析一下柱状图就可以解决问题。可客户协商了一下,这张表只有200万记录,可以立即创建索引,并做表分析,于是我马上执行了下面的脚本:


    >>>>>>>
    在这个SQL种  ---------在这个SQL中;
    >>>>>>>
    可客户协商了一下 ----- 和客户协商了一下。。

    对错别字比较敏感。。。老白莫怪。。

    CLUSTER FACTOR 是哪里看的呀?
    ─ 评论人 白鳝    10-06-08 09:03
      十分感谢,拼音输入法,经常有错别字。CLUSTER FATCOR可以从DBA_INDEXES视图中看,只要做了索引分析就有值的
    ─ 评论人 yaguo_96    10-06-10 11:09
      请大侠指教。您这篇文章里说。clustering_factor增大增加了逻辑读数,因为要在块之间跳来跳去。我通过实验发现确实如此。通过增加一个字段使clustering_factor从200增加到6000,发现一个SQL的逻辑多从100升到了400.
    我想问的是 如果从一个块读完一行,当下一行在同一个块的时候,这个时候不产生逻辑读了吗?
    ─ 评论人 白鳝    10-06-10 13:28
      CLUSTER FACTOR对物理读的影响更大,因为要跳来跳去读,另外如果在一个数据块中能读到多条符合条件的记录,BUFFER GET调用的数量可以减少,比如一个CALL可以找到5条记录,比一个CALL只能找到一条记录,要减少不少BUFFER GET
    ─ 评论人 yaguo_96    10-06-10 14:07
      CLUSTER FACTOR对物理读的影响更大,因为要跳来跳去读,另外如果在一个数据块中能读到多条符合条件的记录,BUFFER GET调用的数量可以减少,比如一个CALL可以找到5条记录,比一个CALL只能找到一条记录,要减少不少BUFFER GET 

    你说的没错。但是通过索引访问表,是先通过索引招到rowid ,在去读表。这只能一条一条访问吧。这时候一个call可以访问多条吗?
    谢谢了。
    ─ 评论人 白鳝    10-06-10 20:36
      具体算法外人就不得而知了,不过从性能考虑,如果索引范围扫描的时候通过ROWID发现连续几条记录在同一个数据块中,在一个CALL中访问从程序设计角度是不难实现的,而且对性能提升有很大帮助。
    ─ 评论人 yaguo_96    10-06-11 10:46
      受益匪浅多谢!
    ─ 评论人 brucewoo    10-06-12 14:48
      学习一下.
    ─ 评论人 tomzzy    10-07-29 16:05
      学习中,老白继续
     
    1
     
    >> 请登录以后评论!您还没有注册?   

    Powered by CWBBS 2.1  © 2005-2006 Cloud Web Soft
      Email:webmaster@justdb.cn