简介
在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge Join,Hash Join这三种物理连接中的一种。理解这三种物理连接是理解在表连接时解决性能问题的基础,下面我来对这三种连接的原理,适用场景进行描述。
嵌套循环连接(Nested Loop Join)
循环嵌套连接是最基本的连接,正如其名所示那样,需要进行循环嵌套,嵌套循环是三种方式中唯一支持不等式连接的方式,这种连接方式的过程可以简单的用下图展示:
图1.循环嵌套连接的第一步
图2.循环嵌套连接的第二步
由 上面两个图不难看出,循环嵌套连接查找内部循环表的次数等于外部循环的行数,当外部循环没有更多的行时,循环嵌套结束。另外,还可以看出,这种连接方式需 要内部循环的表有序(也就是有索引),并且外部循环表的行数要小于内部循环的行数,否则查询分析器就更倾向于Hash Join(会在本文后面讲到)。
通过嵌套循环连接也可以看出,随着数据量的增长这种方式对性能的消耗将呈现出指数级别的增长,所以数据量到一定程度时,查询分析器往往就会采用这种方式。
下面我们通过例子来看一下循环嵌套连接,利用微软的AdventureWorks数据库:
图3.一个简单的嵌套循环连接
图3中ProductID是有索引的,并且在循环的外部表中(Product表)符合ProductID=870的行有4688条,因此,对应的SalesOrderDetail表需要查找4688次。让我们在上面的查询中再考虑另外一个例子,如图4所示。
图4.额外的列带来的额外的书签查找
由 图4中可以看出,由于多选择了一个UnitPrice列,导致了连接的索引无法覆盖所求查询,必须通过书签查找来进行,这也是为什么我们要养成只 Select需要的列的好习惯,为了解决上面的问题,我们既可以用覆盖索引,也可以减少所需的列来避免书签查找。另外,上面符合ProductID的行仅 仅只有5条,所以查询分析器会选择书签查找,假如我们将符合条件的行进行增大,查询分析器会倾向于表扫描(通常来说达到表中行数的1%以上往往就会进行 table scan而不是书签查找,但这并不绝对),如图5所示。
图5.查询分析器选择了表扫描
可 以看出,查询分析器此时选择了表扫描来进行连接,这种方式效率要低下很多,因此好的覆盖索引和Select *都是需要注意的地方。另外,上面情况即使涉及到表扫描,依然是比较理想的情况,更糟糕的情况是使用多个不等式作为连接时,查询分析器即使知道每一个列的 统计分布,但却不知道几个条件的联合分布,从而产生错误的执行计划,如图6所示。
图6.由于无法预估联合分布,导致的偏差
由图6中,我们可以看出,估计的行数和实际的行数存在巨大的偏差,从而应该使用表扫描但查询分析器选择了书签查找,这种情况对性能的影响将会比表扫描更加巨大。具体大到什么程度呢?我们可以通过强制表扫描和查询分析器的默认计划进行比对,如图7所示。
图7.强制表扫描性能反而更好
合并连接(Merge Join)
谈 到合并连接,我突然想起在西雅图参加SQL Pass峰会晚上酒吧排队点酒,由于我和另外一哥们站错了位置,貌似我们两个在插队一样,我赶紧说:I’m sorry,i thought here is end of line。对方无不幽默的说:”It’s OK,In SQL Server,We called it merge join”。
由上面的小故事不难看出,Merge Join其实上就是将两个有序队列进行连接,需要两端都已经有序,所以不必像Loop Join那样不断的查找循环内部的表。其次,Merge Join需要表连接条件中至少有一个等号查询分析器才会去选择Merge Join。
Merge Join的过程我们可以简单用下面图进行描述:
图8.Merge Join第一步
Merge Join首先从两个输入集合中各取第一行,如果匹配,则返回匹配行。加入两行不匹配,则有较小值的输入集合+1,如图9所示。
图9.更小值的输入集合向下进1
用C#代码表示Merge Join的话如代码1所示。
public class MergeJoin
{
// Assume that left and right are already sorted
public static Relation Sort(Relation left, Relation right)
{
Relation output = new Relation();
while (!left.IsPastEnd() && !right.IsPastEnd())
{
if (left.Key == right.Key)
{
output.Add(left.Key);
left.Advance();
right.Advance();
}
else if (left.Key < right.Key)
left.Advance();
else //(left.Key > right.Key)
right.Advance();
}
return output;
}
}
代码1.Merge Join的C#代码表示
因 此,通常来说Merge Join如果输入两端有序,则Merge Join效率会非常高,但是如果需要使用显式Sort来保证有序实现Merge Join的话,那么Hash Join将会是效率更高的选择。但是也有一种例外,那就是查询中存在order by,group by,distinct等可能导致查询分析器不得不进行显式排序,那么对于查询分析器来说,反正都已经进行显式Sort了,何不一石二鸟的直接利用 Sort后的结果进行成本更小的MERGE JOIN?在这种情况下,Merge Join将会是更好的选择。
另外,我们可以由Merge Join的原理看出,当连接条件为不等式(但不包括!=),比如说> < >=等方式时,Merge Join有着更好的效率。
下面我们来看一个简单的Merge Join,这个Merge Join是由聚集索引和非聚集索引来保证Merge Join的两端有序,如图10所示。
图10.由聚集索引和非聚集索引保证输入两端有序
当然,当Order By,Group By时查询分析器不得不用显式Sort,从而可以一箭双雕时,也会选择Merge Join而不是Hash Join,如图11所示。
图11.一箭双雕的Merge Join
哈希匹配(Hash Join)
哈希匹配连接相对前面两种方式更加复杂一些,但是哈希匹配对于大量数据,并且无序的情况下性能均好于Merge Join和Loop Join。对于连接列没有排序的情况下(也就是没有索引),查询分析器会倾向于使用Hash Join。
哈希匹配分为两个阶段,分别为生成和探测阶段,首先是生成阶段,第一阶段生成阶段具体的过程可以如图12所示。
图12.哈希匹配的第一阶段
图 12中,将输入源中的每一个条目经过散列函数的计算都放到不同的Hash Bucket中,其中Hash Function的选择和Hash Bucket的数量都是黑盒,微软并没有公布具体的算法,但我相信已经是非常好的算法了。另外在Hash Bucket之内的条目是无序的。通常来讲,查询优化器都会使用连接两端中比较小的哪个输入集来作为第一阶段的输入源。
接下来是探测阶段,对于另一个输入集合,同样针对每一行进行散列函数,确定其所应在的Hash Bucket,在针对这行和对应Hash Bucket中的每一行进行匹配,如果匹配则返回对应的行。
通 过了解哈希匹配的原理不难看出,哈希匹配涉及到散列函数,所以对CPU的消耗会非常高,此外,在Hash Bucket中的行是无序的,所以输出结果也是无序的。图13是一个典型的哈希匹配,其中查询分析器使用了表数据量比较小的Product表作为生成,而 使用数据量大的SalesOrderDetail表作为探测。
图13.一个典型的哈希匹配连接
上面的情况都是内存可以容纳下生成阶段所需的内存,如果内存吃紧,则还会涉及到Grace哈希匹配和递归哈希匹配,这就可能会用到TempDB从而吃掉大量的IO。这里就不细说了,有兴趣的同学可以移步:http://msdn.microsoft.com/zh-cn/library/aa178403(v=SQL.80).aspx。
总结
下面我们通过一个表格简单总结这几种连接方式的消耗和使用场景:
嵌套循环连接 |
合并连接 |
哈希连接 |
|
适用场景 |
外层循环小,内存循环条件列有序 |
输入两端都有序 |
数据量大,且没有索引 |
CPU |
低 |
低(如果没有显式排序) |
高 |
内存 |
低 |
低(如果没有显式排序) |
高 |
IO |
可能高可能低 |
低 |
可能高可能低 |
理解SQL Server这几种物理连接方式对于性能调优来说必不可少,很多时候当筛选条件多表连接多时,查询分析器就可能不是那么智能了,因此理解这几种连接方式对于定位问题变得尤为重要。此外,我们也可以通过从业务角度减少查询范围来减少低下性能连接的可能性。
本文转自:http://msdn.microsoft.com/zh-cn/library/dn144699.aspx
相关资料:http://blog.csdn.net/tianlesoftware/article/details/5826546
浅谈SQL Server中的三种物理连接操作(HASH JOIN MERGE JOIN NESTED LOOP)的更多相关文章
-
浅谈SQL Server中的三种物理连接操作
简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...
-
浅谈SQL Server中的三种物理连接操作(Nested Loop Join、Merge Join、Hash Join)
简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...
-
SQL Server中的三种物理连接操作
来源:https://msdn.microsoft.com/zh-cn/library/dn144699.aspx 简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Out ...
-
c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
-
SQL Server中的三种Join方式
1.测试数据准备 参考:Sql Server中的表访问方式Table Scan, Index Scan, Index Seek 这篇博客中的实验数据准备.这两篇博客使用了相同的实验数据. 2.SQ ...
-
浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色
简介 在简单恢复模式下,日志文件的作用仅仅是保证了SQL Server事务的ACID属性.并不承担具体的恢复数据的角色.正如”简单”这个词的字面意思一样,数据的备份和恢复仅仅是依赖于手动备份和恢复.在 ...
-
浅谈SQL Server中的事务日志(五)----日志在高可用和灾难恢复中的作用
简介 日志的作用是保证持久性和数据一致性,通过日志可以实现数据的Undo与Redo,因此通过日志,SQL Server不仅仅可以实现灾难恢复,还可以通过日志的Redo来实现高可用性.本篇文章主要讲述日 ...
-
浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架
简介 SQL Server中的事务日志无疑是SQL Server中最重要的部分之一.因为SQL SERVER利用事务日志来确保持久性(Durability)和事务回滚(Rollback).从而还部分确 ...
-
浅谈SQL Server中的快照
原文地址:http://www.cnblogs.com/CareySon/archive/2012/03/30/2424880.html 简介 数据库快照,正如其名称所示那样,是数据库在某一时间点的视 ...
随机推荐
-
javaScript系列:js中获取时间new Date()详细介绍
var myDate = new Date();myDate.getYear(); //获取当前年份(2位)myDate.getFullYear(); //获取完整的年份(4位,1970-????)m ...
-
性能测试之--Apache JMeter安装
Hi,今天给大家分享一下安装JMeter的相关内容~ Apache JMeter 是Apache组织的开源项目,是一个100%纯Java桌面应用,用于压力测试和性能测试. 它能够对HTTP.FTP服务 ...
-
Hadoop随笔(一):工作流程的源码
一.几个可能会用到的属性值 1.mapred.map.tasks.speculative.execution和mapred.reduce.tasks.speculative.execution 这两个 ...
-
MyEclipse导入Maven项目
转自:http://blog.csdn.net/xuelu198708/article/details/8561115 导入分两种方法: 1.使用MyEclipse的普通工程导入,步骤如下: 1> ...
-
项目总结之SSI (一)
这是我进公司之后做的第一个新项目,从无到有. 去年夏天和同事在会议室里度过的日子还恍如昨日啊,时光啊,你慢些走... 作为一个外部项目,美工设计了一个Win8风格的界面,看到现在做前台的同学做的一样是 ...
-
调用 sphinx-build生成HTML文件
安装 Sphinx $ easy_install sphinx Searching for sphinx Reading http://pypi.python.org/simple/sphinx/ R ...
-
hibernate+spring的整合思路加实例(配图解)
首先框架整合我感觉最难的是jar包的引入.因为不同框架的jar容易产生冲突.如果能排除这个因素我想说整合框架还是相对比较容易的. 我整合的框架的一个思想就是:各司其职.因为每个框架处理的事务或者是层次 ...
-
Linux学习历程——Centos 7 find 命令
一.命令介绍 find 命令用于按照指定条件来查找文件. 一些比较常用参数如下表 参数 作用 -name 匹配名称 -perm 匹配权限mode为完全匹配( –mode包含即可) -user 匹配所有 ...
-
Linux安装python2.7
一.Python安装 以Python-2.7.7为例,安装包:Python-2.7.7.tgz如无特殊说明,以下安装步骤都采用root用户执行 1. 解压Python-2.7.7.tgz tar -x ...
-
【洛谷P3960】列队题解
[洛谷P3960]列队题解 题目链接 题意: Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有 n×m ...