暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

sql执行计划引发的思考

原创 星月 2022-09-19
349

问题描述:

开发同事反馈,有个sql 返回结果有的快,有的慢

找到sql:

SELECT test01, test02, test03 FROM (
    SELECT A.PALLET_NO, B.TROLLEY_NO, B.IN_WH_FLAG TROLLEY_WH_FLAG, 
        ROW_NUMBER() OVER(ORDER BY A.OUT_PROCESS_TIME DESC) RN
    FROM test.table1  L
    LEFT JOIN test.table2 A ON 1=1 AND A.RC_NO = '123'
    LEFT JOIN test.table3 B ON A.PALLET_NO = B.PALLET_NO
    WHERE L.LOCATION_ID = '123' AND L.PALLET_FLAG = 'Y'
)
WHERE RN = 1;   ----结果秒出,返回为空


SELECT test01, test02, test03 FROM (
    SELECT A.PALLET_NO, B.TROLLEY_NO, B.IN_WH_FLAG TROLLEY_WH_FLAG, 
        ROW_NUMBER() OVER(ORDER BY A.OUT_PROCESS_TIME DESC) RN
    FROM test.table1  L
    LEFT JOIN test.table2 A ON 1=1 AND A.RC_NO = '234'
    LEFT JOIN test.table3 B ON A.PALLET_NO = B.PALLET_NO
    WHERE L.LOCATION_ID = '234' AND L.PALLET_FLAG = 'Y'
)
WHERE RN = 1;  ----结果5分钟出不来

针对不同的条件,有的值秒出结果,有的值则几分钟出不来

问题排查思路:

1. 固定住执行计划, 加上hint提示,---无解决问题

SELECT test01, test02, test03 FROM (
    SELECT  /*+ LEADING(L,A,B)  index(A INDX_RC_NO) */ A.PALLET_NO, B.TROLLEY_NO, B.IN_WH_FLAG TROLLEY_WH_FLAG, 
        ROW_NUMBER() OVER(ORDER BY A.OUT_PROCESS_TIME DESC) RN
    FROM test.table1  L
    LEFT JOIN test.table2 A ON 1=1 AND A.RC_NO = '234'
    LEFT JOIN test.table3 B ON A.PALLET_NO = B.PALLET_NO
    WHERE L.LOCATION_ID = '234' AND L.PALLET_FLAG = 'Y'
)
WHERE RN = 1;

2.手动代入多个值测试,发现: 当返回为空的时候,都是瞬间返回, 于是怀疑那些出不来的,可能是数据量太大了

3.单独查test.table2的表的rc_no='234' ---可以出结果,返回20000条记录,判断应该不是数据量大的问题

4.根据sql的执行顺序发现,改sql 走的是嵌套 NL , 第一步先去找 L表 WHERE L.LOCATION_ID = '234' AND L.PALLET_FLAG = 'Y' 是否有符合记录的,有则再给table2 /3 进行匹配,如果没有,则直接结束sql,返回空

所以那些返回空的,第一步就已经不符合条件了


结论: 由于嵌套循环的执行顺序为先看子集,也就是驱动表,驱动表有结果,则跟大表去匹配,如果没有,则结束查询

以下是嵌套与哈希的区别:

1. NESTED LOOP

对于被连接的数据子集较小的情况,nested loop连接是个较好的选择。nested loop就是扫描一个表,每读到一条记录,就根据索引去另一个表里面查找,没有索引一般就不会是 nested loops。一般在nested loop中, 驱动表满足条件结果集不大,被驱动表的连接字段要有索引,这样就走nstedloop。如果驱动表返回记录太多,就不适合nested loops了。如果连接字段没有索引,则适合走hash join,因为不需要索引。

可用ordered提示来改变CBO默认的驱动表,可用USE_NL(table_name1 table_name2)提示来强制使用nested loop。

要点如下:

1)对于被连接的数据子集较小的情况,嵌套循环连接是个较好的选择
2)使用USE_NL(table_name1 table_name2)可是强制CBO 执行嵌套循环连接
3)Nested loop一般用在连接的表中有索引,并且索引选择性较好的时候
4)OIN的顺序很重要,驱动表的记录集一定要小,返回结果集的响应时间是最快的。
5)Nested loops 工作方式是从一张表中读取数据,访问另一张表(通常是索引)来做匹配,nested loops适用的场合是当一个关联表比较小的时候,效率会更高。
2. HASH JOIN

hash join是CBO 做大数据集连接时的常用方式。优化器扫描小表(数据源),利用连接键(也就是根据连接字段计算hash 值)在内存中建立hash表,然后扫描大表,每读到一条记录就探测hash表一次,找出与hash表匹配的行。

当小表可以全部放入内存中,其成本接近全表扫描两个表的成本之和。如果表很大不能完全放入内存,这时优化器会将它分割成若干不同的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要有较大的临时段从而尽量提高I/O 的性能。临时段中的分区都需要换进内存做hash join。这时候成本接近于全表扫描小表+分区数*全表扫描大表的代价和。
至于两个表都进行分区,其好处是可以使用parallel query,就是多个进程同时对不同的分区进行join,然后再合并。但是复杂。

使用hash join时,HASH_AREA_SIZE初始化参数必须足够的大,如果是9i,Oracle建议使用SQL工作区自动管理,设置WORKAREA_SIZE_POLICY 为AUTO,然后调整PGA_AGGREGATE_TARGET即可。

以下条件下hash join可能有优势:
1)两个巨大的表之间的连接。
2)在一个巨大的表和一个小表之间的连接。
要点如下:
1)散列连接是CBO 做大数据集连接时的常用方式.
2)也可以用USE_HASH(table_name1 table_name2)提示来强制使用散列连接
3)Hash join在两个表的数据量差别很大的时候.
4)Hash join的工作方式是将一个表(通常是小一点的那个表)做hash运算并存储到hash列表中,从另一个表中抽取记录,做hash运算,到hash 列表中找到相应的值,做匹配。

可用ordered提示来改变CBO默认的驱动表,可用USE_HASH(table_name1 table_name2)提示来强制使用hash join。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论