MySql联接算法
联接算法是MySql数据库用于处理联接的物理策略。在MySql 5.5版本仅支持Nested-Loops Join算法,如果联接表上有索引时,Nested-Loops Join是非常高效的算法。如果有索引时间复杂度为O(N),若没有索引,则可视为最坏的情况,时间复杂度为O(N²)。MySql根据不同的使用场景,支持两种Nested-Loops Join算法,一种是Simple Nested-Loops Join算法,另外一种是Block Nested-Loops Join算法。
Simple Nested-Loops Join算法
从一张表中每次读取一条记录,然后将记录与嵌套表中的记录进行比较。算法如下:
For each row r in R do
For each row s in S do
If r and s satisfy the join condition
Then output the tuple <r, s>
假设在两张表R和S上进行联接的列都不含有索引,算法的扫描次数为:R+RxS,扫描成本为O(RxS)。
假设t1,t2和t3三张表执行INNER JOIN查询,并且每张表使用的联接类型如下:
Table Join Type
t1 range
t2 ref
t3 ALL
如果使用了Simple Nested-Loops Join算法,则算法实现的伪代码如下:
for each row in t1 matching range {
for each row in t2 matching reference key {
for each row in t3 {
if row satisfies join conditions,
send to client
}
}
}
但是当内部表对所联接的列含有索引时,Simple Nested-Loops Join算法可以利用索引的特性来进行快速匹配,此时的算法调整为如下:
For each row r in R do
lookup r in S index
If find s == r
Then output the tuple <r, s>
对于联接的列含有索引的情况,外部表的每条记录不再需要扫描整张内部表,只需要扫描内部表上的索引即可得到联接的判断结果。
在INNER JOIN中,两张联接表的顺序是可以变换的,根据前面描述的Simple Nested-Loops Join算法,优化器在一般情况下总是选择将联接列含有索引的表作为内表。如果两张表R和S在联接列上都有索引,并且索引的高度相同,那么优化器会选择记录数少的表作为外部表,这是因为内部表的扫描次数总是索引的高度,与记录的数量无关。
下面这条SQL语句:
SELECT * FROM driver join user on driver.driver_id = user.uid;
其执行计划如下:

可以看到SQL先查询user表,然后将表driver上的索引和表user上的列uid进行匹配。
这里为什么首先使用user表,因为user表的联接列uid并没有索引,而driver表的联接列driver_id有索引,所以Simple Nested-Loops Join算法将driver表作为内部表。
注意:最终优化器确定联接表的顺序只会按照确切的扫描成本来确定,即:M(外表)+M(外表)*N(内表);这里的外表和内表分别指的是外表和内表的扫描次数,如果含有索引,就是索引B+树的高度,其他一般都是表的记录数。
Block Nested-Loops Join算法
如果联接表没有索引时,Simple Nested-Loops Join算法扫描内部表很多次,执行效率会非常差。而Block Nested-Loops Join算法就是针对没有索引的联接情况设计的,其使用Join Buffer(联接缓存)来减少内部循环取表的次数。
MySql数据库使用Join Buffer的原则如下:
- 系统变量Join_buffer_size决定了Join Buffer的大小。
- Join Buffer可被用于联接是ALL、index、和range的类型。
- 每次联接使用一个Join Buffer,因此多表的联接可以使用多个Join Buffer。
- Join Buffer在联接发生之前进行分配,在SQL语句执行完后进行释放。
- Join Buffer只存储要进行查询操作的相关列数据,而不是整行的记录。
对于上面提到的三个表进行联接操作,如果使用Join Buffer,则算法的伪代码如下:
for each row in t1 matching range {
for each row in t2 matching reference key {
store used columns from t1, t2 in join buffer
if buffer is full {
for each row in t3 {
for each t1, t2 combination in join buffer {
if row satisfies join conditions,
send to client
}
}
empty buffer
}
}
}
if buffer is not empty {
for each row in t3 {
for each t1, t2 combination in join buffer {
if row satisfies join conditions,
send to client
}
}
}
举一个例子,把driver表的_create_date列和user表的create_date列的索引删除,进行联接查询,执行下面的SQL语句:
select _create_date FROM driver join user on driver._create_date = user.create_time;
再次查看SQL执行计划如下:

可以看到,SQL执行计划的Extra列中提示Using join buffer,这就代表使用了Block Nested-Loops Join算法。MySql 5.6会在Extra列显示更为详细的信息,如下面所示:

注意点:在MySql 5.5版本中,Join Buffer只能在INNER JOIN中使用,在OUTER JOIN中则不能使用,即Block Nested-Loops Join算法不支持OUTER JOIN。下面的left join语句:
select _create_date FROM driver left join user on driver._create_date = user.create_time;
在MySql 5.5中的执行计划如下:

可以看到并没有Using join buffer提示,这就意味着没有使用Block Nested-Loops Join算法,但是在MySql 5.6以后开始支持,上面的SQL语句在MySql 5.6中的执行计划如下:

对于上面的SQL语句,使用Block Nested-Loops Join算法需要的时间3.84秒,而不使用的时间是11.93秒。可以看出Block Nested-Loops Join算法对性能提示很多。
Batched Key Access Joins算法
MySql 5.6开始支持Batched Key Access Joins算法(简称BKA),该算法的思想是结合索引和group前面两种方法来提高(search for match)查询比较的操作,以此加快执行效率。
MySQL 5.6.3 implements a method of joining tables called the Batched Key Access (BKA) join algorithm. BKA can be applied when there is an index access to the table produced by the second join operand. Like the BNL join algorithm, the BKA join algorithm employs a join buffer to accumulate the interesting columns of the rows produced by the first operand of the join operation. Then the BKA algorithm builds keys to access the table to be joined for all rows in the buffer and submits these keys in a batch to the database engine for index lookups. The keys are submitted to the engine through the Multi-Range Read (MRR) interface. After submission of the keys, the MRR engine functions perform lookups in the index in an optimal way, fetching the rows of the joined table found by these keys, and starts feeding the BKA join algorithm with matching rows. Each matching row is coupled with a reference to a row in the join buffer.
Batched Key Access Join算法的工作步骤如下:
- 将外部表中相关的列放入Join Buffer中。
- 批量的将Key(索引键值)发送到Multi-Range Read(MRR)接口。
- Multi-Range Read(MRR)通过收到的Key,根据其对应的ROWID进行排序,然后再进行数据的读取操作。
- 返回结果集给客户端。
Batched Key Access Join算法的本质上来说还是Simple Nested-Loops Join算法,其发生的条件为内部表上有索引,并且该索引为非主键,并且联接需要访问内部表主键上的索引。这时Batched Key Access Join算法会调用Multi-Range Read(MRR)接口,批量的进行索引键的匹配和主键索引上获取数据的操作,以此来提高联接的执行效率。
对于Multi-Range Read(MRR)的介绍属于MySql索引的内容,这里简单说明:MySQL 5.6的新特性MRR。这个特性根据rowid顺序地,批量地读取记录,从而提升数据库的整体性能。在MySQL中默认关闭的,如果需要开启:
mysql> SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
Query OK, 0 rows affected (0.00 sec)
总结
MySql 5.6以后越来越多的算法和策略的支持,让联接查询的操作效率越来越快,在学习的时候了解了这些优化效果,更重要的是在实践中明白SQL优化器的工作原理,善于用EXPLAIN等SQL分析命令,对MySql查询有更好的了解。
参考
有不对的地方希望大家多交流,谢谢。
《MySql技术内幕:SQL编程》
http://dev.mysql.com/doc/refman/5.6/en/nested-loop-joins.html
转载请注明出处。
作者:wuxiwei
出处:http://www.cnblogs.com/wxw16/p/6116613.html
MySql联接算法的更多相关文章
- MySQL联接查询算法(NLJ、BNL、BKA、HashJoin)
一.联接过程介绍 为了后面一些测试案例,我们事先创建了两张表,表数据如下: 1 2 3 4 CREATE TABLE t1 (m1 int, n1 char(1)); CREATE TABLE t ...
- SQL夯实基础(九)MySQL联接查询算法
书接上文<SQL夯实基础(八):联接运算符算法归类>. 这里先解释下EXPLAIN 结果中,第一行出现的表就是驱动表(Important!). 对驱动表可以直接排序,对非驱动表(的字段排序 ...
- MySQL联接操作
在MySQL中,联接是一种对表的引用, 多表联接类型: 1.笛卡尔积(交叉联接):在MySQL中为CROSS JOIN或省略JOIN,如: select * from course, teachcou ...
- MySQL Join算法与调优白皮书(一)
正文 Inside君发现很少有人能够完成讲明白MySQL的Join类型与算法,网上流传着的要提升Join性能,加大变量join_buffer_size的谬论更是随处可见.当然,也有一些无知的PGer攻 ...
- MySQL Join算法与调优白皮书(二)
Index Nested-Loop Join (接上篇)由于访问的是辅助索引,如果查询需要访问聚集索引上的列,那么必要需要进行回表取数据,看似每条记录只是多了一次回表操作,但这才是INLJ算法最大 ...
- MySQL Join算法与调优白皮书(三)
Batched Key Access Join Index Nested-Loop Join虽好,但是通过辅助索引进行链接后需要回表,这里需要大量的随机I/O操作.若能优化随机I/O,那么就能极大的提 ...
- MySql分页算法
PERCONA PERFORMANCE CONFERENCE 2009上,来自雅虎的几位工程师带来了一篇"Efficient Pagination Using MySQL"的报告, ...
- nodejs 与 mysql联接
首先安装Mysql 模块吧 npm install mysql 刚开始在网上搜索了一个测试代码,发现根本就连接不上mysql. varClient=require('mysql').Client, c ...
- MySql分析算法作品索引(马上,只是说说而已B-tree)
刚开始学习的时候,百度搜索.但我发现很难理解了很多的太复杂,各种物品的整合总结(建议可能看到的文字,我不明白也没关系,再看看操作步骤图,然后结合文,所以,一切都清楚了很多) B-tree.B这是bal ...
随机推荐
- Android中ListView的几种常见的优化方法
Android中的ListView应该算是布局中几种最常用的组件之一了,使用也十分方便,下面将介绍ListView几种比较常见的优化方法: 首先我们给出一个没有任何优化的Listview的Adapte ...
- 支持“ApplicationDbContext”上下文的模型已在数据库创建后发生更改
异常信息 解决方法: 1.PM> Enable-Migrations 2.打开生成的Configuration.cs文件,修改代码如下 public Configuration() { Auto ...
- javax.mail 发送邮件异常
一.运行过程抛出异常 1.Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/mail/util/ ...
- 初识linux
1.版本 稳定版本:偶数版如2.6.X 发展中的版本:奇数版如2.5.X linux distribution包含:linux kernel + free software + documentati ...
- SweetAlert-js超酷消息警告框插件
简要教程 SweetAlert是一款神奇的javascript弹出消息警告框插件. 来通过一张gif图片看看SweetAlert的效果: 使用方法 要使用该插件,首先要在html的header中引入以 ...
- centos7 apache提供文件下载
1 apache安装 # yum -yinstall httpd //安装httpd会自动安装以下的依赖包: apr apr-util httpd-tools mailcap # rpm -qi ht ...
- 初识Scala反射
我们知道,scala编译器会将scala代码编译成JVM字节码,编译过程中会擦除scala特有的一些类型信息,在scala-2.10以前,只能在scala中利用java的反射机制,但是通过java反射 ...
- tomcat之Session的管理
Session是由服务器端的应用服务器容器(如Tomcat.Jetty)存储的.下面分析一下Tomcat是如何管理Session的. 转自:tomcat架构分析 (Session管理) Tomcat中 ...
- CentOS 6.5 安全加固及性能优化 (转)
通过修改CentOS 6.5 的系统默认设置,对系统进行安全加固,进行系统的性能优化. 环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G) 系统版本:Centos-6.5- ...
- python之路 - 爬虫
网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕 ...