背景

这几天同事写报表,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原理的更多相关文章

  1. MYSQL索引结构原理、性能分析与优化

    [转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...

  2. 深入浅出分析MySQL MyISAM与INNODB索引原理、优缺点、主程面试常问问题详解

    本文浅显的分析了MySQL索引的原理及针对主程面试的一些问题,对各种资料进行了分析总结,分享给大家,希望祝大家早上走上属于自己的"成金之路". 学习知识最好的方式是带着问题去研究所 ...

  3. 深入浅出分析MySQL MyISAM与INNODB索引原理、优缺点分析

    本文浅显的分析了MySQL索引的原理及针对主程面试的一些问题,对各种资料进行了分析总结,分享给大家,希望祝大家早上走上属于自己的"成金之路". 学习知识最好的方式是带着问题去研究所 ...

  4. 【转】由浅入深探究mysql索引结构原理、性能分析与优化

    摘要: 第一部分:基础知识 第二部分:MYISAM和INNODB索引结构 1.简单介绍B-tree B+ tree树 2.MyisAM索引结构 3.Annode索引结构 4.MyisAM索引与Inno ...

  5. 一个最不可思议的MySQL死锁分析

    1    死锁问题背景    1 1.1    一个不可思议的死锁    1 1.1.1    初步分析    3 1.2    如何阅读死锁日志    3 2    死锁原因深入剖析    4 2. ...

  6. MySQL死锁系列-常见加锁场景分析

    在上一篇文章<锁的类型以及加锁原理>主要总结了 MySQL 锁的类型和模式以及基本的加锁原理,今天我们就从原理走向实战,分析常见 SQL 语句的加锁场景.了解了这几种场景,相信小伙伴们也能 ...

  7. <转>一个最不可思议的MySQL死锁分析

    1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2.1 Delete操作的加锁逻辑 4 2.2 死锁预防策略 5 ...

  8. MySQL的JOIN(二):JOIN原理

    表连接算法 Nested Loop Join(NLJ)算法: 首先介绍一种基础算法:NLJ,嵌套循环算法.循环外层是驱动表,循坏内层是被驱动表.驱动表会驱动被驱动表进行连接操作.首先驱动表找到第一条记 ...

  9. left join 原理分析

    left join 原理分析 [转贴 2006-11-15 16:19:50]     字号:大 中 小 案例分析 user表:  id   | name --------- 1   | libk   ...

随机推荐

  1. CentOS Linux最常用命令及快捷键整理

    最近一直在对CentOS系统进行各种体验,为方便自己也方便他人,整理了Linux常用命令及快捷键,不过其实大多和DOS是一样的,只是命令的表达上可能有点儿不一样. 常用Linux命令: 文件和目录: ...

  2. Kubernetes的UI界面Kubernetes Dashboard的搭建

    1.搭建准备 Kubernetes集群的安装部署 2.搭建过程 2.1.在master节点上创建kubernetes-dashboard.yaml cd /etc/kubernetes vim kub ...

  3. des,原理待续

    网络上转载的代码,忘记出处了请原作者见谅! des类 import java.security.*; import javax.crypto.*; /** * DES加解密算法 */ public c ...

  4. memcached的常用命令

    memcached 常用命令及使用说明   1.启动Memcache 常用参数 -p <num> 设置TCP端口号(默认设置为: 11211) -U <num> UDP监听端口 ...

  5. Associate File Type with Qt In Mac Os and Win

    Win Registry Question One day, my boss want me to finish one function which let the users can double ...

  6. mybatis框架中的输入映射

    mybatis.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心. 输入类型: 1.传递简单类型 可以参考我之前的对于数据库增删改查的博文. ...

  7. FW:程序在内存的划分(转)

    一.预备知识—程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈. ...

  8. CSS中float和Clear的使用

    CSS中float和Clear的使用 本文和大家重点讨论一下CSS中Float和Clear属性的使用,一个float对象可以居左或居右,一个设置为float的对象,将根据设置的方向,左移或右移到其父容 ...

  9. cron.c

    /* $OpenBSD: cron.c,v 1.39 2007/02/18 23:59:03 jmc Exp $ */ /* Copyright 1988,1990,1993,1994 by Paul ...

  10. HRBUST1310 火影忍者之~鸣人 2017-03-06 16:01 104人阅读 评论(0) 收藏

    火影忍者之-鸣人 火影忍者的男主角漩涡鸣人,因为身上封印着邪恶的九尾妖狐,无父无母的他受尽了村人的冷眼与歧视,他下定决心要成为第六代火影,让所有人都认同他的存在,火影是动漫火影忍者中主人公鸣人所在的国 ...