由一个场景分析Mysql的join原理
背景
这几天同事写报表,sql语句如下
select * from `sail_marketing`.`mk_coupon_log` a left join `cp0`.`coupon` c on c.code_id = a.coupon_code;
查询出来的结果花了60多秒
数据背景
mk_coupon_log表数据 9368
coupon表数据37735
mk_coupon_log表的coupon_code字段有索引
分析过程
分析原始sql语句
explain
select * from `sail_marketing`.`mk_coupon_log` a left join `cp0`.`coupon` c on c.code_id = a.coupon_code;
type都是ALL,即全表查询,没有用到索引,其中Extra列出现 Using join buffer (Block Nested Loop),从字面意思理解用到了缓存跟循环
其中a表也没用到coupon_code列的索引
一般情况下,第一行称为驱动表,第二行称为被驱动表,从上图可知,a表示驱动表,c表示被驱动表
果断给coupon表的code_id字段加上索引
增加索引之后查询时间0.136秒,分析其sql语句
explain
select * from `sail_marketing`.`mk_coupon_log` a left join `cp0`.`coupon` c on c.code_id = a.coupon_code;
从上图可知,a表驱动c表,c表用到刚加的索引,以及ref到a表的索引,查询效率上急速提高
试试反向left join,查询结果为1.362秒,分析sql
explain select * from `cp0`.`coupon` c left join `sail_marketing`.`mk_coupon_log` a on c.code_id = a.coupon_code
c表驱动a表,用到了a表的索引,也ref到c表的索引,由上可知,都有索引的情况下说明小表驱动大表效率更高
删掉coupon表刚加的索引,还原到原始状况
将left去掉,使用 join连接两个表
select * from `sail_marketing`.`mk_coupon_log` a join `cp0`.`coupon` c on a.coupon_code =c.code_id
查询结果为0.138秒,分析sql语句
explain
select * from `sail_marketing`.`mk_coupon_log` a join `cp0`.`coupon` c on a.coupon_code =c.code_id
去掉left后,发现跟初始的语句explain解析结果不一样了,c表变成驱动表,a表变成被驱动表,查询效率上也急速提升
使用
select * from `sail_marketing`.`mk_coupon_log` a, `cp0`.`coupon` c where c.code_id = a.coupon_code
语句跟上述语句效果一样
从上述结果来看,
1:如果使用left join语句,则left join 前的表设置为驱动表,left join 后的表设置为被驱动表,被驱动表如果没加索引,效率会非常低,Extra列出现using join buffer(block Nested-loop)
2:使用join on,where,inner join on等连接表语句,则mysql会自动优化, 将加索引的表设置为被驱动表,未加索引的表设为驱动表
3:两个表都存在索引的情况下,小表驱动大表查询效果更好
初始sql语句执行效率超过60秒,初步判断原因在于using join buffer(Block nested loop), 查询多方资料,从insidemysql公众号上查到关于join的分析
join原理
join原理参考下图,R是驱动表,S是被驱动表
上图表示R表joinS表, 其中Fetch阶段是指当内表(被驱动表)关联的列是辅助索引时,但是需要访问表中的数据,那么这时就需要再访问主键索引才能得到数据的过程,不论表的存储引擎是InnoDB存储引擎还是MyISAM,这都是无法避免的,只是MyISAM的回表速度要快点,因为其辅助索引存放的就是指向记录的指针,而InnoDB存储引擎是索引组织表,需要再次通过索引查找才能定位数据。
接着计算两张表Join的成本,这里有下列几种概念:
外表(驱动表)的扫描次数,记为O。通常外表的扫描次数都是1,即Join时扫描一次驱动表的数据即可
内表(被驱动表)的扫描次数,记为I。根据不同Join算法,内表的扫描次数不同
读取表的记录数,记为R。根据不同Join算法,读取记录的数量可能不同
Join的比较次数,记为M。根据不同Join算法,比较次数不同
回表的读取记录的数,记为F。若Join的是辅助索引,可能需要回表取得最终的数据
通常来说join的实现由以下三种
1:simple nested-loop join(SNLJ)
算法相当简单,即驱动表(外表r)中的每一条记录与被驱动表(内表s)的记录进行判断
从驱动表中取出R1匹配S表所有列,然后R2,R3,直到将R表中的所有数据匹配完,然后合并数据
可以看到这种方式效率是非常低的,以上述a表数据8000条,c表数据30000条计算,则S*R =2亿多次,开销统计如下
2: index Nested-loop join(INLJ)
我们通常建议在被驱动表建索引,原因就是使用到了index Nested-loop join原理
驱动表中的每条记录通过被驱动表的索引进行访问,因为索引查询的成本是比较固定的,故mysql优化器都倾向于使用记录数少的表作为驱动表(外表)
这种算法在链接查询的时候,驱动表会根据关联字段的索引进行查找,当在索引上找到了符合的值,再回表进行查询,也就是只有当匹配到索引以后才会进行回表。至于驱动表的选择,MySQL优化器一般情况下是会选择记录数少的作为驱动表,但是当SQL特别复杂的时候不排除会出现错误选择。
上表Smatch表示通过索引找到匹配的记录数量。同时可以发现,通过索引可以大幅降低内表(被驱动表)的Join的比较次数,每次比较1条外表的记录,其实就是一次indexlookup(索引查找),而每次index lookup的成本就是树的高度,即IndexHeight。
根据开始的示例,可知如果被驱动表加索引,效率是非常高的,但是开始的示例中加的索引不是主键索引,所以还得进行一次回表查询,辅助索引先查主键索引,然后根据主键索引再查询数据结果, 故如果被驱动表的索引是主键索引,则效率会更高,上述我未尝试,有兴趣的可以自己尝试下
3:block Nested-loop join
通常情况下,mysql会先判断被驱动表是否有索引,如果没有索引的话也肯定不是simple Nested-loop join方式,毕竟代价太大,效率太低,故出现第三种方式
Simple Nested-Loop Join算法的缺点在于其对于内表的扫描次数太多,从而导致扫描的记录太过庞大。Block Nested-Loop Join算法较Simple Nested-Loop Join的改进就在于可以减少内表的扫描次数,仅需扫描内表一次。
Block Nested-Loop Join对比Simple Nested-Loop Join多了一个中间处理的过程,也就是join buffer,使用join buffer将驱动表的查询JOIN相关列都给缓冲到了JOIN BUFFER当中,然后批量与非驱动表进行比较,这也来实现的话,可以将多次比较合并到一次,降低了非驱动表的访问频率。也就是只需要访问一次S表。这样来说的话,就不会出现多次访问非驱动表的情况了,也只有这种情况下才会访问join buffer。
在MySQL当中,我们可以通过参数join_buffer_size来设置join buffer的值,然后再进行操作。默认情况下join_buffer_size=256K,在查找的时候MySQL会将所有的需要的列缓存到join buffer当中,包括select的列,而不是仅仅只缓存关联列。在一个有N个JOIN关联的SQL当中会在执行时候分配N-1个join buffer。
整体效率比较 INLJ > BNLJ > SNLJ
上述分析可知,原始的sql用到了Block Nested-loop 原理,虽然比Simple Nested-loop效率高,但整体而言还是很慢的,
由一个场景分析Mysql的join原理的更多相关文章
- MYSQL索引结构原理、性能分析与优化
[转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...
- 深入浅出分析MySQL MyISAM与INNODB索引原理、优缺点、主程面试常问问题详解
本文浅显的分析了MySQL索引的原理及针对主程面试的一些问题,对各种资料进行了分析总结,分享给大家,希望祝大家早上走上属于自己的"成金之路". 学习知识最好的方式是带着问题去研究所 ...
- 深入浅出分析MySQL MyISAM与INNODB索引原理、优缺点分析
本文浅显的分析了MySQL索引的原理及针对主程面试的一些问题,对各种资料进行了分析总结,分享给大家,希望祝大家早上走上属于自己的"成金之路". 学习知识最好的方式是带着问题去研究所 ...
- 【转】由浅入深探究mysql索引结构原理、性能分析与优化
摘要: 第一部分:基础知识 第二部分:MYISAM和INNODB索引结构 1.简单介绍B-tree B+ tree树 2.MyisAM索引结构 3.Annode索引结构 4.MyisAM索引与Inno ...
- 一个最不可思议的MySQL死锁分析
1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2. ...
- MySQL死锁系列-常见加锁场景分析
在上一篇文章<锁的类型以及加锁原理>主要总结了 MySQL 锁的类型和模式以及基本的加锁原理,今天我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能 ...
- <转>一个最不可思议的MySQL死锁分析
1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2.1 Delete操作的加锁逻辑 4 2.2 死锁预防策略 5 ...
- MySQL的JOIN(二):JOIN原理
表连接算法 Nested Loop Join(NLJ)算法: 首先介绍一种基础算法:NLJ,嵌套循环算法.循环外层是驱动表,循坏内层是被驱动表.驱动表会驱动被驱动表进行连接操作.首先驱动表找到第一条记 ...
- left join 原理分析
left join 原理分析 [转贴 2006-11-15 16:19:50] 字号:大 中 小 案例分析 user表: id | name --------- 1 | libk ...
随机推荐
- 启动tomcat报:No Spring WebApplicationInitializer types detected on classpath
提示找不到web容器,有可能是未加载到spring配置文件,可能是配置文件所在的文件夹未发布或者发布的路径不对导致无法找到 右键web项目,选择properties 查看Deployment Asse ...
- 138. Copy List with Random Pointer (Graph, Map; DFS)
A linked list is given such that each node contains an additional random pointer which could point t ...
- EL表达式获取值栈数据
---------------------siwuxie095 EL 表达式获取值栈数据 1.导入 JSTL 相关包,下载链接: (1)http://tomcat.apache.org/taglibs ...
- RNA -seq
RNA -seq RNA-seq目的.用处::可以帮助我们了解,各种比较条件下,所有基因的表达情况的差异. 比如:正常组织和肿瘤组织的之间的差异:检测药物治疗前后,基因表达的差异:检测发育过程中,不同 ...
- docker 构建 spring boot项目
在docker 开始部署springBoot项目 1.在centos7 ~ 创建一个文件夹docker 里面放置 上面的Dockerfile 和 springBoot 打包的项目docker_spri ...
- Ansible 笔记 (2) - Ad-hoc 命令
先使用 ansible-doc 获取帮助文档 [root@localhost ~]# ansible-doc ping > PING (/usr/lib/python2.7/site-packa ...
- SCI EI期刊
coming soon 关键字:Computer Vision, Computing, Image, Intelligence, IEEE, Compution <Journal of Expe ...
- SQL SERVER 2012数据库:开启防火墙导致外部无法连接数据库解决办法
SQL SERVER 2012数据库:开启防火墙导致外部无法连接数据库解决办法 将以下代码存为OpenSqlServerPort.bat文件: netsh advfirewall firewall a ...
- 团体程序设计天梯赛L1-018 大笨钟 2017-03-22 17:29 79人阅读 评论(0) 收藏
L1-018. 大笨钟 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 微博上有个自称"大笨钟V"的家伙,每 ...
- python + selenium + Js 处理轮动条
selenium并不是万能的,有时候页面上操作无法实现的,这时候就需要借助JS来完成了. 常见场景: 当页面上的元素超过一屏后,想操作屏幕下方的元素,是不能直接定位到,会报元素不可见的. 这时候需要借 ...