本文转发自技术世界,原文连接 http://www.jasongj.com/2015/03/07/Join1/sql
Nested Loop: 对于被链接的数据子集较小的状况,Nested Loop是个较好的选择。Nested Loop就是扫描一个表(外表),每读到一条记录,就根据Join字段上的索引去另外一张表(内表)里面查找,若Join字段上没有索引查询优化器通常就不会选择 Nested Loop。在Nested Loop中,内表(通常是带索引的大表)被外表(也叫“驱动表”,通常为小表——不紧相对其它表为小表,并且记录数的绝对值也较小,不要求有索引)驱动,外表返回的每一行都要在内表中检索找到与它匹配的行,所以整个查询返回的结果集不能太大(大于1 万不适合)。oop
Hash Join: Hash Join是作大数据集链接时的经常使用方式,优化器使用两个表中较小(相对较小)的表利用Join Key在内存中创建散列表,而后扫描较大的表并探测散列表,找出与Hash表匹配的行。 这种方式适用于较小的表彻底能够放于内存中的状况,这样总成本就是访问两个表的成本之和。可是在表很大的状况下并不能彻底放入内存,这时优化器会将它分割成若干不一样的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要求有较大的临时段从而尽可能提升I/O 的性能。它可以很好的工做于没有索引的大表和并行查询的环境中,并提供最好的性能。大多数人都说它是Join的重型升降机。Hash Join只能应用于等值链接(如WHERE A.COL3 = B.COL4),这是由Hash的特色决定的。性能
Merge Join: 一般状况下Hash Join的效果都比排序合并链接要好,然而若是两表已经被排过序,在执行排序合并链接时不须要再排序了,这时Merge Join的性能会优于Hash Join。Merge join的操做一般分三步: 1. 对链接的每一个表作table access full; 2. 对table access full的结果进行排序。 3. 进行merge join对排序结果进行合并。 在全表扫描比索引范围扫描再进行表访问更可取的状况下,Merge Join会比Nested Loop性能更佳。当表特别小或特别巨大的时候,实行全表访问可能会比索引范围扫描更有效。Merge Join的性能开销几乎都在前两步。Merge Join可适于于非等值Join(>,<,>=,<=,可是不包含!=,也即<>)大数据
类别 | Nested Loop | Hash Join | Merge Join |
---|---|---|---|
使用条件 | 任何条件 | 等值链接(=) | 等值或非等值链接(>,<,=,>=,<=),‘<>’除外 |
相关资源 | CPU、磁盘I/O | 内存、临时空间 | 内存、临时空间 |
特色 | 当有高选择性索引或进行限制性搜索时效率比较高,可以快速返回第一次的搜索结果。 | 当缺少索引或者索引条件模糊时,Hash Join比Nested Loop有效。一般比Merge Join快。在数据仓库环境下,若是表的纪录数多,效率高。 | 当缺少索引或者索引条件模糊时,Merge Join比Nested Loop有效。非等值链接时,Merge Join比Hash Join更有效 |
缺点 | 当索引丢失或者查询条件限制不够时,效率很低;当表的纪录数多时,效率低。 | 为创建哈希表,须要大量内存。第一次的结果返回较慢。 | 全部的表都须要排序。它为最优化的吞吐量而设计,而且在结果没有所有找到前不返回数据。 |
本文所作实验均基于PostgreSQL 9.3.5平台优化
一张记录数1万如下的小表nbar.mse_test_test,一张大表165万条记录的大表nbar.nbar_test,大表上建有索引设计
select count(*) from mse_test_test, nbar_test where mse_test_test.client_key = nbar_test.client_key;
以下图所示,执行器将小表mse_test_test做为外表(驱动表),对于其中的每条记录,经过大表(nbar_test)上的索引匹配相应记录。3d
以下图所示,执行器选择一张表将其映射成散列表,再遍历另一张表并从散列表中匹配相应记录。 code
以下图所示,执行器先分别对mse_test_test和nbar_test按client_key排序。其中mse_test_test使用快速排序,而nbar_test使用external merge排序,以后对两者进行Merge Join。 blog
经过对比Query 1 Test 1
,Query 1 Test 2
,Query 1 Test 3
能够看出Nested Loop适用于结果集很小(通常要求小于一万条),而且内表在Join字段上建有索引(这点很是很是很是重要)。排序
以下图所示,执行器经过聚簇索引对大表(nbar_test)排序,直接经过快排对无索引的小表(mse_test_test)排序,以后对二才进行Merge Join。
经过对比Query 1 Test 3
和Query 1 Test 4
能够看出,Merge Join的主要开销是排序开销,若是能经过创建聚簇索引(若是Query必须显示排序),能够极大提升Merge Join的性能。从这两个实验能够看出,建立聚簇索引后,查询时间从4956.768 ms缩减到了1815.238 ms。
以下图所示,执行器经过聚簇索引对大表(nbar_test)和小表(mse_test_test)排序,以后才进行Merge Join。
对比Query 1 Test 4
和Query 1 Test 5
,能够看出两者惟一的不一样在于对小表(mse_test_test)的访问方式不一样,前者使用快排,后者由于聚簇索引的存在而使用Index Only Scan,在表数据量比较小的状况下前者比后者效率更高。由此可看出若是经过索引排序再查找相应的记录比直接在原记录上排序效率还低,则直接在原记录上排序后Merge Join效率更高。
删除nbar_test上的索引
以下图所示,与Query 1 Test 2
相同,执行器选择一张表将其映射成散列表,再遍历另一张表并从散列表中匹配相应记录。
经过对比Query 1 Test 2
,Query 1 Test 6
能够看出Hash Join不要求表在Join字段上创建索引。
mse_test约100万条记录,nbar_test约165万条记录
###**Query 2:**不等值Join
select count(*) from mse_test, nbar_test where mse_test.client_key = nbar_test.client_key and mse_test.client_key between 100000 and 300000;
本次实验经过设置enable_hashjoin=true
,enable_nestloop=false
,enable_mergejoin=false
来试图强制使用Hash Join,可是失败了。