转自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. yrzl-cloud

  2. Coursera Algorithms week2 基础排序 练习测验: Dutch national flag 荷兰国旗问题算法

    第二周课程的Elementray Sorts部分练习测验Interview Questions的第3题荷兰国旗问题很有意思.题目的原文描述如下: Dutch national flag. Given ...

  3. Integer应该用==还是equals

    问题引出:“Integer应该用==还是equals” 讨论这个问题之前我们先放一段代码 public static void main(String[] args) { Integer a1 = 2 ...

  4. 【LuoguP5004】 专心OI - 跳房子

    首先这是一道计数类DP,那我们得先推式子,经过瞎掰乱凑,经过认真分析,我们可以得到这样的方程 F(N)=F(0)+F(1)+....+F(N-M-1) 所有F初值为1,F(1)=2 ANS=F(N+M ...

  5. html body中的标签

    HTML中的标签有两类 一.字体标签 字体标签包含:h1~h6.<font>.<u>.<b>.<strong><em>.<sup> ...

  6. 理解java中的ThreadLocal 专题

    ThreadLocal每一印象: public class IncrementWithStaticVariable{ private static int seqNum = 0; public int ...

  7. SQLServer2008 表连接时null 和 null 无法匹配?

    例如 select * from tbl_a a left join tbl_b b on b.docno=a.docno and b.project=a.project where a.docno= ...

  8. 怪异的Ubuntu

    怪异的Ubuntu 简单记录ubuntu上出现并且网上不好找到甚至压根找不到解决方案的疑难杂症. lvextend扩展逻辑卷的容量不能被系统检测到 问题发生在Ubuntu 16.04系统上. 逻辑卷/ ...

  9. Verification之PSL之use

    1 Where can PSL be used? • Documentation – Requirements – RTL Designs • Controllers – Memories, FIFO ...

  10. QT-Creator+SDK+编译器+自定义配置

    QT4.8的软件曾经耗费巨大的功夫进行构建,不舍得扔掉!重新安装Qt4.8版本 1.安装qt-creator 安装qt-creator-win-opensource-2.4.0.exe版本,不建议使用 ...