zoukankan      html  css  js  c++  java
  • 掀开SQL的神秘面纱,将优化进行到底

    掀开SQL的神秘面纱,将优化进行到底

    有这样一条奇怪的SQL,返回结果不足10行,逻辑读达到1.2w,存在索引却走多次全表扫描,如何揭开它神秘的面纱拯救系统性能,答案在这里,你不可错过!

    本文来自上周四大讲堂课程分享。

    在某运营商的优化经历中曾经遇到了一条比较有意思的SQL。

    该最开始的sql执行情况如下:

    SQL语句:

    执行计划如下:

    统计信息如下:

    针对以上信息我们分析如下:

    1) 该sql每天执行上千次,平均每次执行返回不到10行数据,但是平均逻辑读达到1.2W,可能存在性能问题。

    2)ID为4,5的执行计划路径中出现了两个全表扫描,看到这儿我们可以想到可能是没有合适的索引导致走了全表扫描从而执行效率低下。

    3)ID为2的执行计划路径出现了FILTER,且3,和6为其子路径,如果FILTER有两个及两个以上的子路径,那么他的执行原理将类似于嵌套循环,id号最小的子路径如果返回行数较多,可能会导致多次执行id号更小的子路径,导致性能低下。一般存在“OR EXISTS”的时候会出现此情况,可以根据情况避免。

    4)存在条件“rownum<500”,但是从历史的执行情况来看,返回行数都远小于500行,此处我们先予以忽略。

    处理过程:

    1、进过探查,发现存在两个表都有可用的索引,且两个表都只有几十M的大小。

    2、去掉“OR EXISTS”子句查看执行效率。

    执行计划和统计信息如下:

    此处可用看到,去掉“OR EXISTS”之后两个表走了合适的索引,并且执行效率极高。

    3、去掉“OR EXISTS”中的子句查看执行效率。

    SQL> SELECT A.OFFER_SPEC_GRP_ID

         FROM OFFER_SPEC_GRP_RELA A

         WHERE A.SUB_OFFER_SPEC_ID = 109910000618;

    OFFER_SPEC_GRP_ID

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

            100000048

            109090086

    Elapsed: 00:00:00.01

    执行计划和统计信息如下:

    此处可用看到“OR EXISTS”中的子句单独执行返回行数并不多,且效率依旧很快。

    4、我们把该条sql语句分为“OR EXISTS”的子句和其他部分两块,到此我们可以看到,两块的执行效率都很高,但是合在一起就低了很多。在这种情况下,几乎可以确认,将该存在“OR EXISTS”的子句改写为union必将提升效率。

    执行计划如下:

    统计信息:

    此处我们可以看到,改写之后逻辑读仅仅11,较优化前提升了上千倍。到了此处,我们已经将sql优化到几乎最快的效率了。

    第二次分析,确实改写能够提升效率,但是如果改写sql会涉及到修改代码,当前能否在不修改代码的情况下对其进行优化。

    1)我们再来回顾一下最开始的执行计划路径。

    我们可以看到“OR EXISTS”中的子句是在ID为6的路径才开始执行的,这儿有一个知识点即为一个sql中的子句,一般情况下默认会将其放到最后执行。

    2)ID为4,5的执行计划路径中在有高效索引的情况下却出现了两个全表扫描,可以推断CBO可能没有正常评估执行的cost。

    3)“OR EXISTS”中的子句执行效率很快,返回行数并不多,我们可以考虑提升CBO将其提前执行,看能否影响CBO选择出更高效的执行计划。

    执行计划如下:

    统计信息如下:

    来看另外一种情况:

    执行计划和统计信息如下:

    此处我们在子句中加了一个HINT ,该HINT的作用即使提醒CBO将子句提前执行。

    我们可以看到,执行效率较之前也得到了显著提升,逻辑读降低了7倍作用,虽然相对于改写效率还是高很多,但是在急需处理的情况下该方案还是更加可取的,此时对执行计划进行绑定即可,无需修改代码。

    4)最后执行计划中还是存在全表扫描,我使用hint使其强制走索引查看情况:

    执行计划和统计信息如下:

    此时虽然走了索引,但是却是”INDEX FULL SCAN“,逻辑读也增加了很多,所以此时可以保持之前全表扫描的执行计划。

    索引虽好,但不是万能的,只有结合系统的具体 情况,才能选择性能最佳的SQL。

    案例总结:

    1)当我们看到总的逻辑读除以最后的返回的行数过大时可以认为sql确实是存在性能瓶颈的(有些时候rownum限制除外),但是这没有一个清晰的值来判断过大还是过小,需要凭借优化的经验去评估。但是1200左右逻辑读/条这么夸张的比值还是完全可以判断的,毕竟在某些情况下两三个逻辑读就能获取上百条结果。

    2)很多设计不合理的业务,没有添加合适的索引,可能会导致全表扫描,在某些情况下走全表和走索引产生性能的消耗根本不是一个数量级的。

    3)filter和嵌套循环类似,可以看做是升级版的嵌套循环。而嵌套循环,oracle从较小结果集中读取一行,然后和较大结果集中所有数据逐条进行比较,如果符合规则,就放入结果集中,然后去较小结果集的下一条数据继续进行循环,直到结束。嵌套循环只适合输出较少结果集或者用于快速输出结果集。

    4)某些时候符合我们限制条件的条数过多,但是我们用rownum进行条数限制之后可能会改变其选择执行计划。假如一个sql本来能够返回10000W,但是我用rownum来限制只需要500行。那么优化器会选择能够优先返回500行的执行计划。

  • 相关阅读:
    多个类定义attr属性重复的问题:Attribute "xxx" has already been defined
    好用的批量改名工具——文件批量改名工具V2.0 绿色版
    得到ImageView中drawable显示的区域的计算方法
    得到view坐标的各种方法
    实现类似于QQ空间相册的点击图片放大,再点后缩小回原来位置
    Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat
    Android 自带图标库 android.R.drawable
    解决 Attempting to destroy the window while drawing!
    解决Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 4
    Material Designer的低版本兼容实现(四)—— ToolBar
  • 原文地址:https://www.cnblogs.com/amengduo/p/9587009.html
Copyright © 2011-2022 走看看