前言

前面我们已经了解了MySQLQueryOptimizer的工作原理,学习了Query优化的基本原则和思路,理解了索引选择的技巧,这一节我们将围绕Query语句中使用非常频繁,且随时可能存在性能隐患的Join语句,继续我们的Query优化之旅。

Join 的实现原理

在寻找Join语句的优化思路之前,我们首先要理解在MySQL中是如何来实现Join的,只要理解了实现原理之后,优化就比较简单了。下面我们先分析一下MySQL中Join的实现原理。

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

下面我们将通过一个三表Join语句示例来说明MySQL的NestedLoopJoin实现方式。

注意:由于要展示Explain中的一个在MySQL5.1.18才开始出现的输出信息(在之前版本中只是没有输出信息,实际执行过程并没有变化),所以下面的示例环境是MySQL5.1.26。

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 = 1and m.group_id = g.group_id
and c.group_msg_id = m.id

为了便于示例,我们通过如下操作为group_message表增加了一个group_id的索引:

create index idx_group_message_gid_uid on group_message(group_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.id\G*************************** 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: constrows: 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:

我们可以看出,MySQLQueryOptimizer选择了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比较进行循环查询,才得到最终的结果。

这个过程可以通过如下表达式来表示:

for each record g_rec in table user_group that g_rec.user_id=1{for each record m_rec in group_message that m_rec.group_id=g_rec.group_id{for each record c_rec in group_message_content that c_rec.group_msg_id=m_rec.idpass the (g_rec.user_id, m_rec.subject, c_rec.content) rowcombination to output;} }

下图可以更清晰的标识出实际的执行情况:

假设我们去掉group_message_content表上面的group_msg_id字段的索引,然后再看看执行计划会变成怎样:

sky@localhost : example 11:25:36> drop index idx_group_message_content_msg_id on
group_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.id\G*************************** 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

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

实际上,这里的Join正是利用到了我们在之前“MySQLServer性能优化”一章中所提到的一个Cache参数相关的内容,也就是我们通过join_buffer_size参数所设置的JoinBuffer。

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

当我们使用了JoinBuffer之后,我们可以通过下面的这个表达式描述出示例中我们的Join完成过程:

for each record g_rec in table user_group{ for each record m_rec in group_message that m_rec.group_id=g_rec.group_id{ put (g_rec, m_rec) into the buffer if (buffer is full) flush_buffer();}
}flush_buffer(){ for each record c_rec in group_message_content that  c_rec.group_msg_id = c_rec.id{for each record in the bufferpass (g_rec.user_id, m_rec.subject, c_rec.content) row combination to output; } empty the buffer;
}

当然,如果通过类似于上面的图片来展现或许大家会觉得更容易理解一些,如下:

通过上面的示例,我想大家应该对MySQL中NestedJoin的实现原理有了一个了解了,也应该清楚MySQL使用JoinBuffer的方法了。当然,这里并没有涉及到外连接的内容,实际对于外连接来说,可能存在的区别主要是连接顺序以及组合空值记录方面。

Join 语句的优化

在明白了MySQL中Join的实现原理之后,我们就比较清楚的知道该如何去优化一个一个Join语句了。
1.尽可能减少Join语句中的NestedLoop的循环总次数;如何减少NestedLoop的循环总次数?最有效的办法只有一个,那就是让驱动表的结果集尽可能的小,这也正是在本章第二节中的优化基本原则之一“永远用小结果集驱动大的结果集”。

为什么?因为驱动结果集越大,意味着需要循环的次数越多,也就是说在被驱动结果集上面所需要执行的查询检索次数会越多。比如,当两个表(表A和表B)Join的时候,如果表A通过WHERE条件过滤后有10条记录,而表B有20条记录。如果我们选择表A作为驱动表,也就是被驱动表的结果集为20,那么我们通过Join条件对被驱动表(表B)的比较过滤就会有10次。反之,如果我们选择表B作为驱动表,则需要有20次对表A的比较过滤。

当然,此优化的前提条件是通过Join条件对各个表的每次访问的资源消耗差别不是太大。如果访问存在较大的差别的时候(一般都是因为索引的区别),我们就不能简单的通过结果集的大小来判断需要Join语句的驱动顺序,而是要通过比较循环次数和每次循环所需要的消耗的乘积的大小来得到如何驱动更优化。

2.优先优化NestedLoop的内层循环;

不仅仅是在数据库的Join中应该做的,实际上在我们优化程序语言的时候也有类似的优化原则。内层循环是循环中执行次数最多的,每次循环节约很小的资源,在整个循环中就能节约很大的资源。

3.保证Join语句中被驱动表上Join条件字段已经被索引;
保证被驱动表上Join条件字段已经被索引的目的,正是针对上面两点的考虑,只有让被驱动表的Join条件字段被索引了,才能保证循环中每次查询都能够消耗较少的资源,这也正是优化内层循环的实际优化方法。

4.当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝惜JoinBuffer的设置;

当在某些特殊的环境中,我们的Join必须是All,Index,range或者是index_merge类型的时候,JoinBuffer就会派上用场了。在这种情况下,JoinBuffer的大小将对整个Join语句的消耗起到非常关键的作用

转载于:https://www.cnblogs.com/huiandong/p/9933214.html

mysqljoin的原理和优化相关推荐

  1. 高并发之存储篇:关注下索引原理和优化吧!躲得过实践,躲不过面试官!

    以MySQL的InnoDB索引为主,看完需要5分钟 本文内容预览: 为什么Kafka不需要我们关心索引,而Mysql却需要? Mysql数据怎么被组织 2.1 数据记录最小单位:行 2.2 与磁盘最小 ...

  2. 做diff_Vue3.0时代你必须了解的:diff算法原理和优化

    关注前端公众号 [前端每日一博] 前言 面试官: 你知道 Vue3.0 Object.difineProperty和vue3.0的proxy的区别吗? 你知道 Vue3.0 diff算法原理和它有什么 ...

  3. 12-order by和group by 原理和优化 sort by 倒叙

    ========================原理和优化================================== groupBy分组?会造成一个数据倾斜 1.解决方案:将输入在map端进 ...

  4. Android布局原理与优化

    Android布局原理与优化 目录: 绘制原理 CPU与GPU Android 图形系统的整体架构 RenderThread 硬件加速和软件绘制 invalidate软件绘制流程 invalidate ...

  5. 字节跳动Android三面视频解析:framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等

    前言 对于字节跳动的二面三面而言,Framework+MVP架构+HashMap原理+性能优化+Flutter+源码分析等问题都成高频问点!然而很多的朋友在面试时却答不上或者答不全!今天在这分享下这些 ...

  6. Flink checkpoint操作流程详解与报错调试方法汇总,增量checkpoint原理及版本更新变化,作业恢复和扩缩容原理与优化

    这里写目录标题 flink checkpint出错类型 flink 重启策略 Checkpint 流程简介 增量Checkpoint实现原理 MemoryStateBackend 原理 FsState ...

  7. 直通车运营 直通车扣费原理 直通车优化方案,直通车深度解析

    直通车运营 直通车扣费原理 直通车优化方案,直通车深度解析 今天我们要讲的是怎样将质量得分提高到10分,这也是开直通车需要学习的内容之一.首先需要了解直通车,才能更好的去做优化. 一.直通车的扣费原理 ...

  8. (2)Mysql索引原理和优化总结

    Mysql设计原理栏目总结 (1)Mysql架构和常用引擎介绍 (2)Mysql索引原理和优化总结 1.索引理解和常见面试题 (1)本质 索引是一种能够高效获取数据的数据结构 索引存放在硬盘上 (2) ...

  9. JVM运行原理及优化之 jstat -gc

    我们写好的代码,是要通过JVM才能运行的 JVM 想要执行一个类,首先要加载类,在加载类之前,需要先编译成字节码class 文件 然后就执行类的加载过程,JVM 加载类的话,需要类加载器 类加载器是分 ...

  10. mysql gis index 索引原理_Mysql 索引原理及优化

    Mysql 索引原理及优化 什么是索引 为什么需要索引? 索引是数据表种一个或者多个列进行排序的数据结构 索引能够大幅提升检索速度 创建.更新索引本身也会耗费空间和时间 查找结构进化史 线性查找:一个 ...

最新文章

  1. 谷歌用1.2万个模型“推翻”现有无监督研究成果!斩获ICML 2019最佳论文
  2. GitHub 中文文档正式发布
  3. spring cloud中通过配置文件自定义Ribbon负载均衡策略
  4. 最经典的职场十大理论,好文值得一看滴童鞋们。2
  5. 毕业设计第二次本周目标
  6. VRTK HTC手柄发出射线,瞬移,选择物体 VRTK和steamVR对应版本
  7. 机器学习 流式特征_Web服务与实时机器学习端点的流式传输
  8. petalinux 2020.2 安装教程,基于ubuntu20.04.LTS版本
  9. 域策略(4)——设置统一锁屏壁纸(此策略仅适用于企业版、教育版和 Server SKU版)
  10. Visual Studio Code 编辑器
  11. 马云收购恒生电子几大关键问题
  12. android 指纹是否设置,检查Android是否支持指纹识别以及是否已经录入指纹
  13. 脑机接口专栏 | 如何分析静息状态的fMRI数据?(二)
  14. vue 批量下载图片并打包成zip包
  15. UE4 关闭屏幕显示信息响应
  16. SolidWorks如何编辑今日制造插件生成的齿轮
  17. 疫情之下,互联网产品逆势爆发
  18. 图像处理(3):深度学习之图像分类(垃圾分类案例)
  19. JavaScript整理
  20. u盘启动盘恢复计算机,U盘启动盘怎么恢复正常普通U盘?

热门文章

  1. 决策树(二)--C4.5
  2. 前景检测算法(十七)--基于光流算法
  3. 2012.4.23总结
  4. 最新中科院分区2020_最新中科院分区电气领域SCI期刊
  5. C语言:将数组中的第一个数与最后一个数,第二个数与倒数第二个数依次完成镜像对调
  6. 计算机辅助设计技术案例,【智能科技学院】学院前沿技术运用课程组开展“计算机辅助设计”专题讲座...
  7. Ubuntu18.04 安装 nvidia2080Ti显卡驱动
  8. Spark与MR的区别
  9. C/C++语言传参、返回参数知识点讲解
  10. 深度学习(一)神经网络中的池化与反池化原理