一文搞懂MySQL的join

目录
[TOC]

MySQL的join到底能不能用

经常听到2种观点

  • join性能低,尽量少用
  • 多表join时,变为多个SQL进行多次查询

其实对于上面的观点一定程度上是正确的,但不是完全正确。但之所以流传这么广,主要还是没有搞清楚实际状态,而根据实际使用中总结出来的一些模糊规律。只有了解的MySQL的Join实际执行方式,就会知道上面2种观点是一种模糊的规律,这种规律并不能指导我们实际开发。下面就说说MySQL的实际join执行方式。

MySQL的join是如何执行的

join可以说一种集合的运算,比如left join,right join,inner join,full join,outer join,cross join等,这些集合间的计算关系对应在高中数学集合里面的交集,并集,补集,全集等。但在实际的代码中,join运算基本上是通过多层循环来实现的。

举一个例子,假设有t1,t2两张表,表结构分别如下,

create table t1 (
id int not null AUTO_INCREMENT,
username varchar(20) not null default '',
age int not null default 0,
PRIMARY key (`id`)
)ENGINE=INNODB DEFAULT CHARSET=UTF8MB4  ;

create table t2(
id int not null auto_increment,
username varchar(20) not null default '',
score int not null defalut 0,
primary key (`id`)
))ENGINE=INNODB DEFAULT CHARSET=UTF8MB4;

假设t1有100条数据,t2表有200条数据

查询sql为

select t1.*,t2.* from t1 left join t2 on(t1.username=t2.username)

那么这条SQL的执行步骤如下

  • 从表t1中取一行数据r1
  • 从r1中,取出字段username到表t2中查询
  • 取出表t2中满足条件的行,跟r1组成一行,作为结果集的一部份
  • 重复执行步骤1,2,3,直到表t1的所以数据循环完毕

基本上先遍历t,1,然后根据t1中的每行数据中的username,去表t2中查找满足条件的记录。基本就是2层循环。

如何优化join查询

从上面可以看出,join本质是循环,这里的开销如下

  1. 遍历t1数据,读取数据为t1表的行数,假设行数为n,则复杂度也为n
  2. 根据t1的匹配字段username去t2中一行一行的查询数据
    这个过程,因为MySQL的数据存储结构为二叉树,时间复杂度为log2(m) m为t2表的总行数
  3. 那么总复杂度近似为 n+n(2log2(m))

从上面的步骤可以看出,优化方向,

  • 降低t1查询时的开销,主要是磁盘io开销,避免全表扫描,用索引
  • 降低t2查询时的开销,也用索引
  • 将数据量多的表做被驱动表,小表作驱动表,m取了对数,大表数据量大对复杂度的影响没有线性增长
  • 缓存t1表,不用每次去磁盘load,比如一次缓存100条,那么能显著降低磁盘读数据次数,t2每次与缓存中的t1数据进行比较
  • 随机磁盘读比较耗费磁盘性能,转为顺序读,因为二叉树的存储结构,每次非主键查找,有一个回表的动作,即根据主键再次查询需要的数据

优化的基本方法

  • 减少循环次数,减少磁盘IO次数,变随机IO为顺序IO

其实MySQL针对上面的优化方法有对应的算法,

  • Simple Nested Loop Join 最普通的循环,这个要避免
  • Block Nested Loop Join 主要是针对t2表上没有索引,在步骤2将t2中的每一行数据跟join buffer数据做对比,这样将磁盘操作转为内存操作进行比较,但是如果被驱动表的数据比较大的话,也影响性能,主要是cache pool被占满,导致MySQL性能下降
  • Index Nested Join 就是都通过主键进行查找关联,这种性能比较好
  • Batched Key Access Join 这个是 Index Nested Join上做的优化,因为回表的存在,随机操作io也很耗费性能,这个算法的核心在于通过辅助索引去查找时,将得到的主键进行排序,然后按照主键递增的顺序进行查找,对磁盘的读接近顺序读,从而优化性能

到底要不用Join

从上面的分析我们可以看到,用Join还是可行的,只要性能可控且在接受范围内,还是能减少代码复杂度的。需要避免的是join的表没有索引,不然这样的SQL发线上是灾难性的。

总结

Join还是可以大胆的使用,只要把握好几个原则

  • 尽量让join的列是索引列,而且最好是类型相同,尽可能是主键索引
  • 尽量将小表做驱动表(这一点MySQL在5.6某个版本后能自动完成)
  • 养成将写好的SQL进行explain的好习惯,观察SQL的执行过程
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268