您的位置:首页 > 运维架构

将“相关值”导致的Nested Loop优化成Hash Join

2011-04-12 16:54 387 查看
原文地址:http://www.itpub.net/thread-1376537-1-2.html

“相关值”是我剽窃的一个词,与之相反的是“不相关值”,我借用来描述以下这种情况:在SQL中有两个表A和B要join,join条件是A.name=B.first_name,判定符“=”左右两边的值是“不相关”的,而substr(A.name,1,length(B.first_name))=B.first_name,判定符“=”左右两边的值是“相关”的,因为要通过“=”右边的值才能求出左边的值(相反亦然)。

由于这种“相关值”的情况,A、B表join的算法注定只能走NestedLoop了,因为每一个不同的驱动值(驱动表中的值,也就是B表中的值)将改变A表负责join的值,执行计划如下:

1
select
a.*,b.object_name
from
TEST_OBJECTa,TEST_OBJ1b
2
where
substr(a.object_name,1,length(b.object_name))=b.object_name;
01
已选择108612行。
02
03
已用时间:00:00:21.54
04
05
---------------------------------------------------------------------
06
|Id|Operation|Name|Rows|Bytes|Cost|
07
---------------------------------------------------------------------
08
|0|SELECTSTATEMENT|||||
09
|1|NESTEDLOOPS|||||
10
|2|TABLEACCESSFULL|TEST_OBJ1||||
11
|*3|TABLEACCESSFULL|TEST_OBJECT||||
12
---------------------------------------------------------------------
13
14
PredicateInformation(identifiedbyoperationid):
15
---------------------------------------------------
16
17
3-filter("B"."OBJECT_NAME"=SUBSTR("A"."OBJECT_NAME",1,LENGTH("B"."OBJ
18
ECT_NAME")))
19
20
统计信息
21
----------------------------------------------------------
22
1recursivecalls
23
0dbblockgets
24
9262734consistentgets
这里展开一下,NestedLoop这个算法复杂度是O(B*A)(B表示驱动表的记录数,A表示内部表),如果A、B都很大,那就杯具了,关于NestedLoop驱动表的选取就不展开了。

网友anlinew提供了一种非常精妙的,决的优化方法,将语句改写成:

1
select
a.*,b.object_name
from
TEST_OBJECTa,TEST_OBJ1b
2
where
substr(a.object_name,1,length(b.object_name))=b.object_name
3
and
substr(a.object_name,1,4)=substr(b.object_name,1,4)
4
and
length(b.object_name)>3
5
union
all
6
select
a.*,b.object_name
from
TEST_OBJECTa,TEST_OBJ1b
7
where
substr(a.object_name,1,length(b.object_name))=b.object_name
8
and
length(b.object_name)<4;
1
01
已选择108612行。
02
03
已用时间:00:06:51.24
04
05
-----------------------------------------------------------------------------------
06
|Id|Operation|Name|Rows|Bytes|Cost(%CPU)|Time|
07
-----------------------------------------------------------------------------------
08
|0|SELECTSTATEMENT||3468|365K|90997(100)|00:18:12|
09
|1|UNION-ALL||||||
10
|*2|HASHJOIN||34|3672|176(2)|00:00:03|
11
|*3|TABLEACCESSFULL|TEST_OBJ1|659|12521|35(0)|00:00:01|
12
|4|TABLEACCESSFULL|TEST_OBJECT|52544|4566K|139(1)|00:00:02|
13
|5|NESTEDLOOPS||3434|362K|90821(1)|00:18:10|
14
|*6|TABLEACCESSFULL|TEST_OBJ1|659|12521|35(0)|00:00:01|
15
|*7|TABLEACCESSFULL|TEST_OBJECT|5|445|138(1)|00:00:02|
16
-----------------------------------------------------------------------------------
17
18
PredicateInformation(identifiedbyoperationid):
19
---------------------------------------------------
20
21
2-access(SUBSTR("A"."OBJECT_NAME",1,4)=SUBSTR("B"."OBJECT_NAME",1,4))
22
filter("B"."OBJECT_NAME"=SUBSTR("A"."OBJECT_NAME",1,LENGTH("B"."OBJE
23
CT_NAME")))
24
3-filter(LENGTH("B"."OBJECT_NAME")>3)
25
6-filter(LENGTH("B"."OBJECT_NAME")<4)
26
7-filter("B"."OBJECT_NAME"=SUBSTR("A"."OBJECT_NAME",1,LENGTH("B"."OBJE
27
CT_NAME")))
28
29
统计信息
30
----------------------------------------------------------
31
1recursivecalls
32
0dbblockgets
33
33946consistentgets
执行计划虽然变复杂了,但是耗时大幅减少,consistentgets也大幅降低,作出巨大贡献的是HashJoin的引入。

这里再展开一下,HashJoin的复杂度是O(A+B),简单来说就是对A、B表各扫描一次,如果A、B都比较大的情况来看,无疑HashJoin要比NestedLoop优越很多。

扯远了,回到anlinew的具体方法上吧,导致HashJoin出现的关键因素是一个谓词的引入:

1
and
substr(a.object_name,1,4)=substr(b.object_name,1,4)
套用我剽窃的那个词来说,这是“不相关值”的对比!

anlinew的核心思想是将数据“分片”,该例子中分片的依据是多少位首字母(这里是4),其中“大头”由HashJoin处理,而“小头”走NestedLoop,这种“抓大放小”的做法直接就从复杂度上进行了优化。

这种“分片”的思想非常值得借鉴,将“相关值”判断转化成“不相关值”的判断也是处理问题的一种有效手法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: