转自http://www.kuqin.com/database/20081206/29717.html 简朝阳

JOIN的用法你真的知道吗?

在 MySQL 中,只有一种 Join 算法,就是大名鼎鼎的 Nested Loop Join,他没有其他很多数据库所提供的 Hash Join,也没有 Sort Merge Join。顾名思义,Nested Loop Join 实际上就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。如果还有第三个参与 Join,则再通过前两个表的 Join 结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此往复。

还是通过示例和图解来说明吧,后面将通过我个人数据库测试环境中的一个 example(自行设计,非MySQL 自己提供) 数据库中的三个表的 Join 查询来进行示例。

注意:由于这里有些内容需要在MySQL 5.1.18之后的版本中才会体现出来,所以本测试的MySQL 版本为5.1.26

表结构:

sky@localhost : example 11:09:32> show create table user_groupG
*************************** 1. row ***************************
Table: user_group
Create Table: CREATE TABLE `user_group` (
`user_id` int(11) NOT NULL,
`group_id` int(11) NOT NULL,
`user_type` int(11) NOT NULL,
`gmt_create` datetime NOT NULL,
`gmt_modified` datetime NOT NULL,
`status` varchar(16) NOT NULL,
KEY `idx_user_group_uid` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

insert into `user_group`(`user_id`,`group_id`,`user_type`,`gmt_create`,`gmt_modified`,`status`) values (1001,10001,1,'2017-04-19 00:00:00','2017-04-19 00:00:00','1'),(1002,10001,2,'2017-04-19 00:00:00','2017-04-19 00:00:00','2'),(1003,10001,2,'2017-04-19 00:00:00','2017-04-19 00:00:00','1'),(1004,10002,1,'2017-04-19 00:00:00','2017-04-19 00:00:00','3'),(1005,10002,1,'2017-04-19 00:00:00','2017-04-19 00:00:00','2'),(1006,10001,1,'2017-04-19 00:00:00','2017-04-19 00:00:00','2'),(1007,10003,2,'2017-04-19 00:00:00','2017-04-19 00:00:00','1');

sky@localhost : example 11:10:32> show create table group_messageG
*************************** 1. row ***************************
Table: group_message
Create Table: CREATE TABLE `group_message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`gmt_create` datetime NOT NULL,
`gmt_modified` datetime NOT NULL,
`group_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`author` varchar(32) NOT NULL,
`subject` varchar(128) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_group_message_author_subject` (`author`,`subject`(16)),
KEY `idx_group_message_author` (`author`),
KEY `idx_group_message_gid_uid` (`group_id`,`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=97 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

insert into `group_message`(`id`,`gmt_create`,`gmt_modified`,`group_id`,`user_id`,`author`,`subject`) values (1,'2017-04-19 00:00:00','2017-04-19 00:00:00',10001,1001,'dd','dd'),(2,'2017-04-19 00:00:00','2017-04-19 00:00:00',10002,1002,'sss','sss'),(3,'2017-04-19 00:00:00','2017-04-19 00:00:00',10003,1003,'eeee','eee');

sky@localhost : example 11:10:43> show create table group_message_contentG
*************************** 1. row ***************************
Table: group_message_content
Create Table: CREATE TABLE `group_message_content` (
`group_msg_id` int(11) NOT NULL,
`content` text NOT NULL,
KEY `group_message_content_msg_id` (`group_msg_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

insert into `group_message_content`(`group_msg_id`,`content`) values (1,'fdafdafdafds'),(2,'fafdasddddddddddddddddddddddddddddddddddddddddddd');

使用Query如下:

select m.subject msg_subject, c.content msg_content
from user_group g,group_message m,group_message_content c
where g.user_id = 1
and m.group_id = g.group_id
and c.group_msg_id = m.id

看看我们的 Query 的执行计划:

sky@localhost : example 11:17:04> explain select m.subject msg_subject, c.contentmsg_content
-> from user_group g,group_message m,group_message_content c
-> where g.user_id = 1
-> and m.group_id = g.group_id
-> and c.group_msg_id = m.idG
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: g
type: ref
possible_keys: user_group_gid_ind,user_group_uid_ind,user_group_gid_uid_ind
key: user_group_uid_ind
key_len: 4
ref: const
rows: 2
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: m
type: ref
possible_keys: PRIMARY,idx_group_message_gid_uid
key: idx_group_message_gid_uid
key_len: 4
ref: example.g.group_id
rows: 3
Extra:
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: c
type: ref
possible_keys: idx_group_message_content_msg_id
key: idx_group_message_content_msg_id
key_len: 4
ref: example.m.id
rows: 2
Extra:

我们可以看出,MySQL Query Optimizer 选择了 user_group 作为驱动表,首先利用我们传入的条件 user_id 通过 该表上面的索引 user_group_uid_ind 来进行 const 条件的索引 ref 查找,然后以 user_group 表中过滤出来的结果集的 group_id 字段作为查询条件,对 group_message 循环查询,然后再通过 user_group 和 group_message 两个表的结果集中的  group_message 的 id 作为条件 与 group_message_content 的 group_msg_id 比较进行循环查询,才得到最终的结果。没啥特别的,后一个引用前一个的结果集作为条件,实现过程可以通过下图表示:

下面的我们调整一下 group_message_content 去掉上面的 idx_group_message_content_msg_id 这个索引,然后再看看会是什么效果:

sky@localhost : example 11:25:36> drop index idx_group_message_content_msg_id ongroup_message_content;
Query OK, 96 rows affected (0.11 sec)
 
sky@localhost : example 10:21:06> explain
-> select m.subject msg_subject, c.content msg_content
-> from user_group g,group_message m,group_message_content c
-> where g.user_id = 1
-> and m.group_id = g.group_id
-> and c.group_msg_id = m.idG
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: g
type: ref
possible_keys: idx_user_group_uid
key: idx_user_group_uid
key_len: 4
ref: const
rows: 2
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: m
type: ref
possible_keys: PRIMARY,idx_group_message_gid_uid
key: idx_group_message_gid_uid
key_len: 4
ref: example.g.group_id
rows: 3
Extra:
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: c
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 96
Extra: Using where; Using join buffer

我们看到不仅仅 group_message_content 表的访问从 ref 变成了 ALL,此外,在最后一行的 Extra信息从没有任何内容变成为  Using where; Using join buffer,也就是说,对于从 ref 变成 ALL 很容易理解,没有可以使用的索引的索引了嘛,当然得进行全表扫描了,Using where 也是因为变成全表扫描之后,我们需要取得的 content 字段只能通过对表中的数据进行 where 过滤才能取得,但是后面出现的 Using join buffer 是一个啥呢?

我们知道,MySQL 中有一个供我们设置的参数 join_buffer_size ,这里实际上就是使用到了通过该参数所设置的 Buffer 区域。那为啥之前的执行计划中没有用到呢?

实际上,Join Buffer 只有当我们的 Join 类型为 ALL(如示例中),index,rang 或者是 index_merge 的时候 才能够使用,所以,在我们去掉 group_message_content 表的 group_msg_id 字段的索引之前,由于 Join 是 ref 类型的,所以我们的执行计划中并没有看到有使用 Join Buffer。

当我们使用了 Join Buffer 之后,我们可以通过下面的这张图片来表示 Join 完成过程:

本文出自:http://www.jianzhaoyang.com/database/mysql_join_buffer_nested_loop_implement

0419MySQL 中 Join 的基本实现原理的更多相关文章

  1. 【原创】大数据基础之Spark(8)Spark中Join实现原理

    spark中join有两种,一种是RDD的join,一种是sql中的join,分别来看: 1 RDD join org.apache.spark.rdd.PairRDDFunctions /** * ...

  2. 关于python多线程编程中join()和setDaemon()的一点儿探究

    关于python多线程编程中join()和setDaemon()的用法,这两天我看网上的资料看得头晕脑涨也没看懂,干脆就做一个实验来看看吧. 首先是编写实验的基础代码,创建一个名为MyThread的  ...

  3. 学习重点:1、金典的设计模式在实际中应用2、JVM原理3、jui源代码

    学习重点:1.金典的设计模式在实际中应用 2.JVM原理 3.jui源代码

  4. Linq中join & group join & left join 的用法

    Linq中join & group join & left join 的用法 2013-01-30 11:12 12154人阅读 评论(0) 收藏 举报  分类: C#(14)  文章 ...

  5. Oracle中join left,join right,inner join,(+) 等

    Oracle中join left,join right,inner join,(+) 等 博客分类: Oracle   建表create table TEST1create table TEST1(  ...

  6. org.apache.commons.lang.StringUtils 中 Join 函数

    转自 http://my.oschina.net/zenglingfan/blog/134872 写代码的时候,经常会碰到需要把一个List中的每个元素,按逗号分隔转成字符串的需求,以前是自己写一段比 ...

  7. Spring中EmptyResultDataAccessException异常产生的原理及处理方法

    Spring中EmptyResultDataAccessException异常产生的原理及处理方法 Spring中使用JdbcTemplate的queryForObject方法,当查不到数据时会抛出如 ...

  8. 基于接口回调详解JUC中Callable和FutureTask实现原理

    Callable接口和FutureTask实现类,是JUC(Java Util Concurrent)包中很重要的两个技术实现,它们使获取多线程运行结果成为可能.它们底层的实现,就是基于接口回调技术. ...

  9. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

随机推荐

  1. Makefile 文件怎么写

    跟我一起写Makefile:MakeFile介绍 Makefile 使用总结 1. make 命令与 Makefile 文件 在 Linux 平台,执行 make 命令时,会在当前目录下寻找 Make ...

  2. 【撸码caffe四】 solver.cpp&&sgd_solver.cpp

    caffe中solver的作用就是交替低啊用前向(forward)算法和后向(backward)算法来更新参数,从而最小化loss,实际上就是一种迭代的优化算法. solver.cpp中的Solver ...

  3. 图结构练习—BFSDFS—判断可达性(BFS)

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2138 注意该图为有向图,1000个点应该最多有 ...

  4. php的self和this

    <?phpclass ss{ public static $code; public function __construct( $code ) { self::$code=$code; //这 ...

  5. poj3071Football(概率期望dp)

    Football Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5620   Accepted: 2868 Descript ...

  6. Django 创建新项目后要完成的几个步骤

    首先,在过一遍创建新项目的步骤: -创建一个新项目 -建了数据库后要确定自己是用 mysql数据库  还是用 sqlite3数据库 -如果是mysql数据库,那一堆配置 -如果是sqlite3数据库, ...

  7. 网络简要<入门篇>对应配置代码

    交换机的配置 (1)交换机的模式: switch>    用户模式,可以查看设备的部分内容 SW-3ceng>enable SW-3ceng#          进入特权模式,可以查看更多 ...

  8. Websocket实现前后台通信,demo小测试

    新需求大概如下:用户登录系统,登录成功之后建立websocket连接,实现通信 总体思路:前端不是我负责,只是简单的做个功能,先实现登录,把用户标识存入HttpSeesion,再建立websocket ...

  9. java常见面试题03-String,StringBuffer,StringBuilder的区别

    面试题   A:String,StringBuffer,StringBuilder的区别 1:String 内容不可变,StringBuffer.StringBudiler可变  2:StringBu ...

  10. 1.0 windows10系统安装步骤(1)

    1.0 windows10系统安装步骤(1) 根据自己对笔记本系统的折腾,为了方便他人系统的安装,故总结笔记本系统的安装步骤 目录: 1.0 [windows10系统安装步骤(1)] 2.0 Linu ...