写在前面

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

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

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

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

联表查询的基础知识

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

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

第一个查询:

  1. EXPLAIN extended
  2. SELECT ads.id FROM ads, city
  3. WHERE
  4. city.city_id = 8005
  5. AND ads.status = 'online'
  6. AND city.ads_id=ads.id
  7. ORDER BY ads.id desc

执行计划为:

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

第二个查询:

  1. EXPLAIN extended
  2. SELECT ads.id FROM ads,city
  3. WHERE
  4. city.city_id =8005
  5. AND ads.status = 'online'
  6. AND city.ads_id=ads.id
  7. ORDER BY city.ads_id desc

执行计划里没有了using temporary:

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

为什么?DBA告诉我们:

  1. 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 表要少得多。慢查实例如下:

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

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

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

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

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

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

如何优化?

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

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

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

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

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

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

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

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

  1. id select_type table type possible_keys key key_len ref rows Extra

  2. 1 SIMPLE mb index userid PRIMARY 4 (NULL) 10

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

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

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

  1. explain

  2. SELECT mb.id……

  3. FROM mb,mbei,u

  4. WHERE

  5. mb.id=mbei.mb_id

  6. and mb.uid=u.user_id

  7. order by mbei.apply_time desc

  8. limit 0,10

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

  1. id select_type table type possible_keys key key_len ref rows Extra

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

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

  4. 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. QString够绕的,分为存储(编译器)和解码(运行期),还有VS编译器的自作主张,还有QT5的变化

    多读几篇,每篇取几句精华加深我对QString的理解. ------------------------------------------------------------------ QStri ...

  2. Sinowal Bootkit 分析-中国红客网络技术联盟 - Powered by Discuz!

    訪问原文 (一)模块组成         感染过Sinowal的电脑,Sinaowal在硬盘中的分布例如以下图: ; Sector                 Offset             ...

  3. <% %> in html

    $(document).on('click', '.invoiceNumber', function () { var string = <%= StaticHelper.GetCurrentC ...

  4. hdoj--3339--In Action(最短路+01背包)

    In Action Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  5. C#中大批量导入数据SqlBulkCopy

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...

  6. pc端和移动端的轮播图实现(只是结构,内容以后慢慢补充)

    轮播图 PC端 移动端 原生js的写法 图片顺序 8123456781 设置计时器 当过度完成之后判断index是否到达两边界限,是的话设置位移 手指touchstart时,获取位置,暂停计时器 手指 ...

  7. 智能家居控制APPUI界面设计

    2017年,随着智能化产业进入新的市场格局,千家品牌实验室也迎来全新的升级,致力为智能产业生态链提供更全更新更深度的行业分析和品牌数据监测服务.本文为大家带来关于中国智能家居行业发展APP设计欣赏. ...

  8. mac os x install redis-3.2.9

    下载.解压.重命名并且编译安装Redis~ wget http://download.redis.io/releases/redis-3.2.9.tar.gz ~ tar xzf redis-3.2. ...

  9. Verilog之openMSP430(1)

    openMSP430_IO interrupt Verilog file: omsp_gpio.v //================================================ ...

  10. 「Redis 笔记」常用命令

    编号 命令 描述 1 DEL key 此命令删除一个指定键(如果存在). 2 DUMP key 此命令返回存储在指定键的值的序列化版本. 3 EXISTS key 此命令检查键是否存在. 4 EXPI ...