写在前面

1、不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程

2、不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小

3、但把mysql客户端(如SQLyog,如HeidiSQL)放在桌面上,时不时拿出来 explain 一把,这是一种美德!

在实例讲解之前,我们先回顾一下联表查询的基础知识

联表查询的基础知识

引子:为什么第一个查询using temporary,第二个查询不用临时表呢?

下面两个查询,它们只差了一个order by,效果却迥然不同。

第一个查询:

EXPLAIN extended
SELECT ads.id FROM ads, city
WHERE
city.city_id = 8005
AND ads.status = 'online'
AND city.ads_id=ads.id
ORDER BY ads.id desc

执行计划为:

id  select_type table  type    possible_keys  key          key_len  ref             rows  filtered  Extra
1
SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using temporary; Using filesort
1
SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where

第二个查询:

EXPLAIN extended
SELECT ads.id FROM ads,city
WHERE
city.city_id =8005
AND ads.status = 'online'
AND city.ads_id=ads.id
ORDER BY city.ads_id desc

执行计划里没有了using temporary:

id  select_type table type    possible_keys  key           key_len  ref              rows  filtered  Extra
1
SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using where; Using filesort
1
SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where

为什么?DBA告诉我们:

MySQL 表关联的算法是 Nest Loop Join,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。

EXPLAIN 结果中,第一行出现的表就是驱动表(Important!)

以上两个查询语句,驱动表都是 city,如上面的执行计划所示!

对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序(Important!)

因此,order by ads.id desc 时,就要先 using temporary 了!

驱动表的定义

wwh999 在 2006年总结说,当进行多表连接查询时, [驱动表] 的定义为:

1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表]

2)未指定联接条件时,行数少的表为[驱动表](Important!)

忠告:如果你搞不清楚该让谁做驱动表、谁 join 谁,请让 MySQL 运行时自行判断

既然“未指定联接条件时,行数少的表为[驱动表]”了,而且你也对自己写出的复杂的 Nested Loop Join 不太有把握(如下面的实例所示),就别指定谁 left/right join 谁了,请交给 MySQL优化器 运行时决定吧。

如果您对自己特别有信心,可以像火丁一样做优化

小结果集驱动大结果集

de.cel 在2012年总结说,不管是你,还是 MySQL,优化的目标是尽可能减少JOIN中Nested Loop的循环次数。

以此保证:永远用小结果集驱动大结果集(Important!)!

实例讲解

Nested Loop Join慢查SQL语句

先了解一下 mb 表有 千万级记录,mbei 表要少得多。慢查实例如下:

explain
SELECT mb.id, ……
FROMmb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mbei.apply_time DESC
limit 0,10

够复杂吧。Nested Loop Join 就是这样,以驱动表的结果集作为循环的基础数据,然后将结果集中的数据作为过滤条件一条条地到下一个表中查询数据,最后合并结果。

此时还有第三个表,则将前两个表的 Join 结果集作为循环基础数据,再一次通过循环查询条件到第三个表中查询数据,如此反复。

这条语句的执行计划如下:

id select_type table   type    possible_keys   key           key_len  ref       rows       Extra
1
SIMPLE mb index userid userid 4 (NULL) 6060455 Using index; Using temporary; Using filesort
1
SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1
1
SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

由于动用了“LEFT JOIN”,所以攻城狮已经指定了驱动表,虽然这张驱动表的结果集记录数达到百万级!

如何优化?

优化第一步:LEFT JOIN改为JOIN,干嘛要 left join 啊?直接 join!

explain
SELECT mb.id……
FROM mb JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mbei.apply_time DESC
limit 0,10

立竿见影,驱动表立刻变为小表 mbei 了, Using temporary 消失了,影响行数少多了:

id select_type table  type     possible_keys     key         key_len  ref                rows    Extra
1
SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13383 Using filesort
1
SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1
SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

优化第一步之分支1:根据驱动表的字段排序,好吗?

left join不变。干嘛要根据非驱动表的字段排序呢?我们前面说过“对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序!”的。

explain
SELECT mb.id……
FROM mb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mb.id DESC
limit 0,10

也满足业务场景,做到了rows最小:

id select_type table  type     possible_keys  key           key_len  ref        rows  Extra

1 SIMPLE mb index userid PRIMARY 4 (NULL) 10

1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 Using index

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

优化第二步:去除所有JOIN,让MySQL自行决定!写这么多密密麻麻的 left join/inner join 很开心吗?

explain

SELECT mb.id……

FROM mb,mbei,u

WHERE

mb.id=mbei.mb_id

and mb.uid=u.user_id

order by mbei.apply_time desc

limit 0,10

立竿见影,驱动表一样是小表 mbei:

id select_type table   type    possible_keys      key          key_len  ref                rows    Extra

1 SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13388 Using filesort

1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1

1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

最后的总结:

强调再强调:

1、不要过于相信你的运气!

2、不要相信你的开发环境里SQL的执行速度!

3、请拿起 explain 武器,如果你看到以下现象,请优化:

1)出现了Using temporary

2)rows过多,或者几乎是全表的记录数

3)key 是 (NULL)

4)possible_keys 出现过多(待选)索引

记住,explain 是一种美德!

【explain】MySQL联表查询中的驱动表的更多相关文章

  1. 了解MySQL联表查询中的驱动表,优化查询,以小表驱动大表

    一.为什么要用小表驱动大表 1.驱动表的定义 当进行多表连接查询时, [驱动表] 的定义为: 1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表] 2)未指定联接条件时,行数少的表为[驱动表 ...

  2. [慢查优化]联表查询注意谁是驱动表 & 你搞不清楚谁join谁更好时请放手让mysql自行判定

    写在前面的话: 不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程: 不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小: ...

  3. 【转】[慢查优化]联表查询注意谁是驱动表 & 你搞不清楚谁join谁更好时请放手让mysql自行判定

    转自:http://zhengyun-ustc.iteye.com/blog/1942797 写在前面的话: 不要求每个人一定理解 联表查询(join/left join/inner join等)时的 ...

  4. MySQL数据库之单表查询中关键字的执行顺序

    目录 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 2 执行顺序 3 关键字使用语法 MySQL数据库之单表查询中关键字的执行顺序 1 语法顺序 select distinct from ...

  5. MySQL多表查询之外键、表连接、子查询、索引

    MySQL多表查询之外键.表连接.子查询.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复的,不允许为 ...

  6. 分析比较多表查询中的IN与JOIN

    IN 是子查询的关键字,JOIN 是连接的关键字,项目开发中经常会使用到多表查询,而子查询与连接正是实现多表查询的重要途径.那两者是怎么运行的?IN与JOIN哪个更好?下面就来分析与比较. 现在有te ...

  7. Mysql Join-连接查询(中)

    Mysql Join-连接查询(中) 认识 就我平时的数据接触来看, 连接查询也没有很复杂,不够是非常需要耐心和逻辑的, 一点点将数据查出来, 拼接等. 没有什么技巧, 多练习就会了. 无非就是表之间 ...

  8. 将一个多表关联的条件查询中的多表通过 create select 转化成一张单表的sql、改为会话级别临时表 【我】

    将一个多表关联的条件查询中的多表通过 create   select  转化成一张单表的sql 将结果改为创建一个会话级别的临时表: -- 根据下面这两个sql CREATE TABLE revenu ...

  9. 【杂记】mysql 左右连接查询中的NULL的数据筛选问题,查询NULL设置默认值,DATE_FORMAT函数

    MySQL左右连接查询中的NULL的数据筛选问题 xpression 为 Null,则 IsNull 将返回 True:否则 IsNull 将返回 False. 如果 expression 由多个变量 ...

随机推荐

  1. 《从零開始学Swift》学习笔记(Day48)——类型检查与转换

    原创文章,欢迎转载.转载请注明:关东升的博客 继承会发生在子类和父类之间,是一系列类的继承关系. 比如:Person是类层次结构中的根类.Student是Person的直接子类.Worker是Pers ...

  2. A. Music(Codeforces Round #315 (Div. 2) 求最大的容纳量)

    A. Music time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...

  3. 固定管线shader编写:基本属性

    欢迎转载!转载时请注明出处:http://write.blog.csdn.net/postedit/50753008 shader 部分介绍: properties:属性部分 material:材质部 ...

  4. ES Segment Memory——本质上就是segment中加到内存的FST数据,因此segment越多,该内存越大

    ElasticSearch优化系列四:ES的heap是如何被瓜分掉的 转自:https://www.jianshu.com/p/f41b706db6c7 以下分别解读几个我知道的内存消耗大户: Seg ...

  5. python spark 随机森林入门demo

    class pyspark.mllib.tree.RandomForest[source] Learning algorithm for a random forest model for class ...

  6. hdoj--1281--棋盘游戏(最小点覆盖+枚举)

    棋盘游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  7. php !=和!==

    今天测试了一下!=和!== <?phpheader("Content-type: text/html; charset=utf-8"); if (1!="1&quo ...

  8. Linux下打包、压缩和解压

    命令使用:tar  主选项+辅选项  压缩后文件名  要压缩的文件 -c 建立压缩文件(常用) -x 解压(常用) -t 查看压缩文件(常用) -r 向压缩文件末尾追加文件 -u 更新一个压缩包中的文 ...

  9. 了解jQuery的$符号

    $是什么? 可以使用typeof关键字来观察$的本质. console.log(type of $); //输出结果为function 因此可以得出结论,$其实就是一个函数.$(); 只是根据所给参数 ...

  10. 极光推送设置标签和别名无效的解决办法:JPush设置别名不走成功回调

    极光推送设置标签和别名无效的解决办法 JPush设置别名不走成功回调的解决办法 http://www.cnblogs.com/chenqitao/p/5506023.html 主要是网络加载过快导致的 ...