转自:http://zhengyun-ustc.iteye.com/blog/1942797

写在前面的话:

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

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

但把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  

最后的总结:
强调再强调:
不要过于相信你的运气!
不要相信你的开发环境里SQL的执行速度!
请拿起 explain 武器,
如果你看到以下现象,请优化:
  • 出现了Using temporary;
  • rows过多,或者几乎是全表的记录数;
  • key 是 (NULL);
  • possible_keys 出现过多(待选)索引。
 
记住,explain 是一种美德!

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

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

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

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

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

  3. 【explain】MySQL联表查询中的驱动表

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

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

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

  5. python 之 Django框架(orm单表查询、orm多表查询、聚合查询、分组查询、F查询、 Q查询、事务、Django ORM执行原生SQL)

    12.329 orm单表查询 import os if __name__ == '__main__': # 指定当前py脚本需要加载的Django项目配置信息 os.environ.setdefaul ...

  6. mysql重点,表查询操作和多表查询

    表单查询 1. 完整的查询语句语法 select distinct(* or 字段名 or 四则运算 )from 表名 where 条件 group by 条件 having 条件 order by ...

  7. 如何让in/exists 子查询(半连接)作为驱动表?

    一哥们问我,怎么才能让子查询作为驱动表? SQL如下: select rowid rid from its_car_pass7 v where 1 = 1 and pass_datetime > ...

  8. 关于mysql,需要掌握的基础(一):CRUD、存储引擎、单表查询相关、多表查询join、事务并发、权限管理等等

    目录 关于mysql,需要掌握的基础(一): 1.了解数据库sql.数据库系统.数据库管理系统的概念. 2.了解DDL.DML.DQL语句是什么? 3.了解存储引擎.存储引擎[InnoDB 和 MyI ...

  9. MySQL数据库学习笔记(六)----MySQL多表查询之外键、表连接、子查询、索引

    本章主要内容: 一.外键 二.表连接 三.子查询 四.索引 一.外键: 1.什么是外键 2.外键语法 3.外键的条件 4.添加外键 5.删除外键 1.什么是外键: 主键:是唯一标识一条记录,不能有重复 ...

随机推荐

  1. Dom操作--跑马灯效果

    这里给园友们演示的是Dom操作实现跑马灯效果,相信我们很多人都用Winform实现过跑马灯效果,其中的关键就是Tirm控件,那么在Dom操作中是用setInterval方法来实现隔一段时间执行一段代码 ...

  2. wpf+xml实现的一个随机生成早晚餐的小demo

    话说每到吃完的时间就发愁,真的不知道该吃什么,然后就想到做一个生成吃什么的小软件,既然这个软件如此的简单,就打算用wpf开发吧,也不用数据库了,直接保存在xml中就可以了 程序整体结构如下图 首先我写 ...

  3. IntelliJ IDEA 使用随笔

    注册的地址:http://idea.iteblog.com/key.php

  4. Razor模板引擎

    Razor模板引擎 阅读目录 一.简介 二.非Mvc中使用Razor 三.总结 回到目录 一.简介 在MVC以外的场景中,我们往往需要完成一些模板引擎生成代码或页面的工作:在以前我们一般常用的有Raz ...

  5. RESTheart官方文档

    作者:Andrea Di Cesare, Maurizio Turatti RESTHeart是SoftInstigate公司创建并开发的开源项目. MongoDB的WEB操作接口 RESTHEART ...

  6. ios开发之C语言第3天

    变量的命名规则以及规范 变量的命名规则  1>变量名只能由任意的字母,下划线和$以及数字组成,注意不能用数字开头 2>区分大小写 3>变量一定要先定义再使用 4>同一个大括号中 ...

  7. No Hibernate Session bound to thread, and configuration does not allow creat

    No Hibernate Session bound to thread, and configuration does not allow creat 今天遇到这么一个错误,在网上差了很多都没有能解 ...

  8. BZOJ 1592: [Usaco2008 Feb]Making the Grade 路面修整

    Description FJ打算好好修一下农场中某条凹凸不平的土路.按奶牛们的要求,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中. 整条路被分成了 ...

  9. QLGame 2D Engine编写环境搭建

    QLGame 2D Engine编写 (win7环境搭建) 广州麒麟网络工作室,计划制作一款2d game engine,基于opengl(es)平台,暂时支持android,以后考虑支持linux, ...

  10. Mysql table ful

    http://ourmysql.com/archives/1327 http://blog.csdn.net/kevon_sun/article/details/7967728 http://my.o ...