sql优化 表连接join方式
sql优化核心 是数据库中 解析器+优化器的工作,我觉得主要有以下几个大方面:
1>扫表的方法(索引非索引、主键非主键、书签查、索引下推)
2>关联表的方法(三种),关键是内存如何利用
3>处理排序聚合的方法,如何利用内存
即 少扫磁盘多用内存
1>扫表的方法(索引非索引、主键非主键、书签查、索引下推)
2>关联表的方法(三种),关键是内存如何利用
3>处理排序聚合的方法,如何利用内存
即 少扫磁盘多用内存
--=====2 表关联方式
-----0 概述
类别 Nested Loop Hash Join Merge Join
使用条件 任何条件 等值连接(=) 等值或非等值连接(>,<,=,>=,<=),‘<>’除外
相关资源 CPU、磁盘I/O 内存、临时空间 内存、临时空间
Nested Loop 优点: 当有高选择性索引或进行限制性搜索时效率比较高,能够快速返回第一次的搜索结果。
缺点: 当索引丢失或查询条件限制不够时,效率低;当表纪录数多时,效率低
实现: 从一张表中读取数据,访问另一张表(通常是索引)来做匹配,nested loops适用的场合是当一个关联表比较小的时候,效率会更高
Hash Join 优点: 当无索引或索引条件模糊时,Hash Join比Nested Loop有效。通常比Merge Join快。在数据仓库环境下,如果表的纪录数多,效率高
缺点: 为建立哈希表,需要大量内存。第一次的结果返回较慢
实现: 将一个表(通常是小一点的那个表)做hash运算,将列数据存储到hash列表中,从另一个表中抽取记录,做hash运算,到hash 列表中找到相应的值,做匹配。
Merge Join 优点: 当无索引或索引条件模糊时,Merge Join比Nested Loop有效。非等值连接时,Merge Join比Hash Join更有效
缺点: 所有的表都需要排序。它为最优化的吞吐量而设计,并且在结果没有全部找到前不返回数据
实现: 先将关联表的关联列各自做排序,然后从各自的排序表中抽取数据,到另一个排序表中做匹配,因为merge join需要做更多的排序,所以消耗的资源更多
通常来讲,能够使用merge join的地方,hash join都可以发挥更好的性能
选择什么连接类型有以下三要素:
1) 表大小
2) 连接列是否有索引
3) 连接列是否要排序
不同DBMS对表连接的支持:
1) SqlServer, Oracle支持以下三种连接
2) Mysql5.5前支持NestedLoop,之后支持对其的优化算法Block Nested-Loop
-----1 Nested Loop Join
驱动表(outer table),另一个为inner table,驱动表中的每一行与inner表中的相应记录JOIN。类似一个嵌套的循环。适用于驱动表的记录集比较小(<10000)且inner表的连接列上要有Index。
注意:驱动表的记录集一定要小,inner表的连接列要有(UniqueIndex更好)索引。处理过程伪代码:
for oi in count(outer table 行数):do
for ii in count(inner table 行数):do --若inner连接列有主键索引,则不用循环inner表,也不需要回表,效率超高
if oi.column=ii.column:do --若只是普通索引,还需要回表查相应数据(可能需要大量的随机IO),可能会慢许多,但也比没索引强
Send To Client
fi
done
done
--Block Nested-Loop Join(仅Mysql支持)
因为普通Nested-Loop一次只将一行传入内层循环, 所以outer table (的结果集)有多少行, 内存循环便要执行多少次.
在inner table的连接上有索引的情况下,其扫描成本为O(Rn),若没有索引,则扫描成本为O(Rn*Sn)。如果inner table有很多记录,则Nested-Loops Join会扫描内部表很多次,执行效率会非常差。
BNL算法:将outer table结果集存入join buffer, 内层循环的每一行与整个Join buffer中的记录做比较,从而减少内层循环的次数。
举例,outer table结果集是100行,使用NLJ 需扫描内部表100次,如使用BNL,先把Outer Loop表每次读取的10行记录放到join buffer,然后在InnerLoop表中直接匹配这10行数据,
内存循环就可以一次与这10行进行比较, 这样只需要比较10次,对内部表的扫描减少了9/10。所以BNL算法就能够显著减少内层循环表扫描的次数.
MySQL使用Join Buffer有以下要点:
当MySQL的 Join 有使用到 Block Nested-Loop Join,那么调大变量join_buffer_size 才是有意义的。而前面的 Index Nested-Loop Join如果仅使用索引进行Join,那么调大这个变量则毫无意义
a) 只有在join类型为all, index, range的时候才可以使用join buffer。
b) 能够被buffer的每一个join都会分配一个buffer, 也就是说一个query最终可能会使用多个join buffer。
c) 第一个nonconst table不会分配join buffer, 即便其扫描类型是all或者index。
d) 在join之前就会分配join buffer, 在query执行完毕即释放。
e) join buffer中只会保存参与join的列, 并非整个数据行。
f) 5.6版本及以后,优化器管理参数optimizer_switch中的block_nested_loop控制着BNL是否被用于优化器。默认条件下是开启,若果设置为off,优化器在选择 join方式的时候会选择NLJ算法。
-----2 Hash Join
将两表中较小的在内存中构造一个HASH表(只对连接列),扫描另一个表,同样对JOIN KEY进行HASH后探测是否可以JOIN。适用于记录集比较大的情况。
需要注意的是:如果HASH表太大,无法一次构造在内存中,则分成若干个partition,写入磁盘的temporary segment,则会多一个写的代价,会降低效率
-----3 Sort Merge Join
通常情况下Hash Join的效果都比排序合并连接要好,然而如果行源已经被排过序,在执行排序合并连接时不需要再排序了,这时排序合并连接的性能会优于散列连接。
可以使用USE_MERGE(table_name1 table_name2)来强制使用排序合并连接.
Sort Merge join 用在没有索引,并且数据已经排序的情况.
将两个表排序,然后将两个表合并。通常情况下,只有在以下情况发生时,才会使用此种JOIN方式:
1.RBO模式 且 HASH_JOIN_ENABLED=false
2.不等价关联(>,<,>=,<=,<>)
3.数据源已排序