前文说了EXPLAIN的输出的含义,本文实战一下。

Database Schema

DROP DATABASE dbTest;
CREATE DATABASE dbTest;
USE dbTest;
CREATE TABLE t1
(
c_primary_key               INT,
c_unique_key                CHAR(64),
c_unique_not_null_key       CHAR(64) NOT NULL,
c_key                       CHAR(64),
c_multi_key_part1           CHAR(64),
c_multi_key_part2           CHAR(64),
c_int_value                 INT,
c_str_value                 CHAR(64),
PRIMARY KEY(c_primary_key),
UNIQUE KEY(c_unique_key),
UNIQUE KEY(c_unique_not_null_key),
KEY(c_multi_key_part1, c_multi_key_part2),
KEY(c_key)
)ENGINE=InnoDB;
CREATE TABLE t2
(
c_primary_key               INT,
c_unique_key                CHAR(64),
c_unique_not_null_key       CHAR(64) NOT NULL,
c_key                       CHAR(64),
c_multi_key_part1           CHAR(64),
c_multi_key_part2           CHAR(64),
c_int_value                 INT,
c_str_value                 CHAR(64),
c_t1_primary_key            INT,
PRIMARY KEY(c_primary_key),
UNIQUE KEY(c_unique_key),
UNIQUE KEY(c_unique_not_null_key),
KEY(c_multi_key_part1, c_multi_key_part2),
KEY(c_key),
UNIQUE KEY(c_t1_primary_key)
)ENGINE=InnoDB;

Join类型

const
  1. 使用primary key查找一条记录,满足const条件。

    mysql> EXPLAIN SELECT * FROM dbTest.t1 WHERE c_primary_key=1;
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    |  1 | SIMPLE      | t1    | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    
  2. 使用unique key查找一条非NULL记录,满足const条件。

    mysql> EXPLAIN SELECT * FROM dbTest.t1 WHERE c_unique_key='4cb15758c8e311e5b46f06af68695f49';
    +----+-------------+-------+-------+---------------+--------------+---------+-------+------+-------+
    | id | select_type | table | type  | possible_keys | key          | key_len | ref   | rows | Extra |
    +----+-------------+-------+-------+---------------+--------------+---------+-------+------+-------+
    |  1 | SIMPLE      | t1    | const | c_unique_key  | c_unique_key | 65      | const |    1 | NULL  |
    +----+-------------+-------+-------+---------------+--------------+---------+-------+------+-------+
    
  3. 使用unique key查找NULL记录,这种情况下NULL记录可能有多条,所以不满足const条件。而是ref条件,ref意味着可能得到匹配的结果不唯一,即可能存在多条。那么对于unique key,可以为NULL,那么我们再来看:

    mysql> EXPLAIN SELECT * FROM dbTest.t1 WHERE c_unique_key is NULL;
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
    | id | select_type | table | type | possible_keys | key          | key_len | ref   | rows | Extra                 |
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
    |  1 | SIMPLE      | t1    | ref  | c_unique_key  | c_unique_key | 65      | const |    1 | Using index condition |
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
    
  4. 使用非NULL的unique key来查询,跟primary key类似,都是唯一的,所以满足const条件。

    mysql> EXPLAIN SELECT * FROM dbTest.t1 WHERE c_unique_not_null_key='00047412c96511e5844906af68695f49' limit 1;
    +----+-------------+-------+-------+-----------------------+-----------------------+---------+-------+------+-------+
    | id | select_type | table | type  | possible_keys         | key                   | key_len | ref   | rows | Extra |
    +----+-------------+-------+-------+-----------------------+-----------------------+---------+-------+------+-------+
    |  1 | SIMPLE      | t1    | const | c_unique_not_null_key | c_unique_not_null_key | 64      | const |    1 | NULL  |
    +----+-------------+-------+-------+-----------------------+-----------------------+---------+-------+------+-------+
    
  5. 使用非唯一的index列查询,可能存在多条记录,所以是ref而不是const。

    mysql> EXPLAIN SELECT * FROM dbTest.t1 WHERE c_key='4cb15758c8e311e5b46f06af68695f49';
    +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
    | id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
    +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
    |  1 | SIMPLE      | t1    | ref  | c_key         | c_key | 65      | const |    1 | Using index condition |
    +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
    

    const至多有一条记录满足条件!

eq_ref

前文说,eq_ref类型对于之前表的每一个行组合,只从该表中读取一条记录。只有一条记录匹配要求,索引必须是primary key或者unique key(非NULL)。

  1. 使用primary key进行表关联

    mysql> EXPLAIN SELECT * FROM dbTest.t1,dbTest.t2 WHERE t1.c_primary_key=t2.c_key;
    +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
    | id | select_type | table | type   | possible_keys | key     | key_len | ref             | rows | Extra       |
    +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
    |  1 | SIMPLE      | t2    | ALL    | c_key         | NULL    | NULL    | NULL            | 8042 | Using where |
    |  1 | SIMPLE      | t1    | eq_ref | PRIMARY       | PRIMARY | 4       | dbTest.t2.c_key |    1 | Using where |
    +----+-------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
    

    可以看到,对于t2表中的每一行,t1中都有唯一的一行(至多一行)进行匹配,所以最终匹配为eq_ref。

  2. 使用NOT NULL的unique key(唯一的一行)进行关联

    mysql> EXPLAIN SELECT * FROM dbTest.t1,dbTest.t2 WHERE t1.c_unique_not_null_key=t2.c_unique_not_null_key;
    +----+-------------+-------+--------+-----------------------+-----------------------+---------+---------------------------------+------+-------+
    | id | select_type | table | type   | possible_keys         | key                   | key_len | ref                             | rows | Extra |
    +----+-------------+-------+--------+-----------------------+-----------------------+---------+---------------------------------+------+-------+
    |  1 | SIMPLE      | t1    | ALL    | c_unique_not_null_key | NULL                  | NULL    | NULL                            | 9307 | NULL  |
    |  1 | SIMPLE      | t2    | eq_ref | c_unique_not_null_key | c_unique_not_null_key | 64      | dbTest.t1.c_unique_not_null_key |    1 | NULL  |
    +----+-------------+-------+--------+-----------------------+-----------------------+---------+---------------------------------+------+-------+
    

    eq_ref至多有一条记录满足条件!

ref

对于之前表的每一个组合,匹配到索引值的所有记录将被读取。例如匹配那些左侧前缀的key(multi-part key),或者非primary key,或者非unique index(匹配值不是NULL),或者unique index(但是匹配值是NULL)。

  1. 使用index列匹配多行记录

    mysql> EXPLAIN SELECT * FROM t1 WHERE t1.c_key='58a95fbac96511e5844906';
    +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
    | id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                 |
    +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
    |  1 | SIMPLE      | t1    | ref  | c_key         | c_key | 65      | const |    1 | Using index condition |
    +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
    
  2. 多表关联时使用index列匹配多行记录

    mysql> EXPLAIN SELECT * FROM t1,t2 WHERE t1.c_unique_key=t2.c_key;
    +----+-------------+-------+------+---------------+-------+---------+------------------------+------+-------------+
    | id | select_type | table | type | possible_keys | key   | key_len | ref                    | rows | Extra       |
    +----+-------------+-------+------+---------------+-------+---------+------------------------+------+-------------+
    |  1 | SIMPLE      | t1    | ALL  | c_unique_key  | NULL  | NULL    | NULL                   | 9307 | Using where |
    |  1 | SIMPLE      | t2    | ref  | c_key         | c_key | 65      | dbTest.t1.c_unique_key |    1 | NULL        |
    +----+-------------+-------+------+---------------+-------+---------+------------------------+------+-------------+
    
  3. 匹配左侧前缀(t1.c_multi_key_part1)的多行记录

    mysql> EXPLAIN SELECT * FROM t1,t2 WHERE t1.c_multi_key_part1=t2.c_key;
    +----+-------------+-------+------+-------------------+-------------------+---------+-----------------+------+-------------+
    | id | select_type | table | type | possible_keys     | key               | key_len | ref             | rows | Extra       |
    +----+-------------+-------+------+-------------------+-------------------+---------+-----------------+------+-------------+
    |  1 | SIMPLE      | t2    | ALL  | c_key             | NULL              | NULL    | NULL            | 4904 | Using where |
    |  1 | SIMPLE      | t1    | ref  | c_multi_key_part1 | c_multi_key_part1 | 65      | dbTest.t2.c_key |    1 | NULL        |
    +----+-------------+-------+------+-------------------+-------------------+---------+-----------------+------+-------------+
    
  4. 使用unique index查找NULL的记录

    mysql> EXPLAIN SELECT * FROM t1 WHERE t1.c_unique_key is NULL;
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
    | id | select_type | table | type | possible_keys | key          | key_len | ref   | rows | Extra                 |
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
    |  1 | SIMPLE      | t1    | ref  | c_unique_key  | c_unique_key | 65      | const |    1 | Using index condition |
    +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
    

    ref可以匹配多行记录!

ref_or_null

从这个关键词可以看出,ref或者NULL,既在ref的基础上加上NULL的搜索。以下例子对应于ref的记录。

  1. 使用index列匹配多行记录,或者NULL记录

    mysql> EXPLAIN SELECT * FROM t1 WHERE t1.c_key='58a95fbac96511e5844906' or t1.c_key is NULL;
    +----+-------------+-------+-------------+---------------+-------+---------+-------+------+-----------------------+
    | id | select_type | table | type        | possible_keys | key   | key_len | ref   | rows | Extra                 |
    +----+-------------+-------+-------------+---------------+-------+---------+-------+------+-----------------------+
    |  1 | SIMPLE      | t1    | ref_or_null | c_key         | c_key | 65      | const |    2 | Using index condition |
    +----+-------------+-------+-------------+---------------+-------+---------+-------+------+-----------------------+
    
  2. 其余不再给输出,参照ref,SQL如下。

    mysql> EXPLAIN SELECT * FROM t1,t2 WHERE t1.c_unique_key=t2.c_key or t1.c_unique_key is NULL;
    mysql> EXPLAIN SELECT * FROM t1,t2 WHERE t1.c_multi_key_part1=t2.c_key or t1.c_multi_key_part1 is NULL;
    

    ref_or_null = ref + NULL记录,所以是多行

range

对于一个给定的range,使用index来获取记录。range可以使用=,<>,>,>=,BETWEEN,IN()等操作符。

  1. BETWEEN操作符

    mysql> EXPLAIN SELECT * FROM t1 WHERE t1.c_primary_key BETWEEN 10 AND 20;
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |   10 | Using where |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    
  2. IN操作符

    mysql> EXPLAIN SELECT * FROM t1 WHERE t1.c_primary_key IN (10, 20);
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    2 | Using where |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    

等等,不再赘述。

range使用index,多条记录!

index

该类型跟ALL类型,但是不同之处在于搜索的是index数据,因为index数据比较小,所以效率肯定比ALL要高。分为两种情况:

  1. 索引数据足够满足要求,即索引数据包括了查询的所有数据(在InnoDB下,索引数据数据存储的内容,包括 索引列+主键列,如下,因为c_key索引数据包括了c_key列值+c_primary_key列值,所以只需遍历索引即可)

    mysql> EXPLAIN SELECT c_primary_key,c_key FROM t1;
    +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | index | NULL          | c_key | 65      | NULL | 4915 | Using index |
    +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
    
  2. 需要根据某个索引列的顺序进行查询,如下。第一条EXPLAIN从t1中select出所有的c_primary_key,这是不需要用到c_key(当然用c_key是也可以的)。但是第二条EXPLAIN由于需要按照c_key的进行排序(而c_key index就是有序的),所以只需要c_key index存储的顺序读取出来即可达到排序的功能,所以使用c_key就起到了排序的作用。

    mysql> EXPLAIN SELECT c_primary_key FROM t1;
    +----+-------------+-------+-------+---------------+-----------------------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key                   | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+-----------------------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | index | NULL          | c_unique_not_null_key | 64      | NULL | 4915 | Using index |
    +----+-------------+-------+-------+---------------+-----------------------+---------+------+------+-------------+
    mysql> EXPLAIN SELECT c_primary_key FROM t1 order by c_key;
    +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key   | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | index | NULL          | c_key | 65      | NULL | 4915 | Using index |
    +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
    

    index这种情况也就比ALL要好一点,这种SQL需要重点review,以防带来灾难!

ALL

终于到了最差的情况,全表扫描。即没有合适的索引数据,所以只能一行一行的扫描数据了,沦落至此,可想而知效果极差。例如:

  1. 查询符合条件的记录,如下,由于c_str_value列没有索引,导致只能进行全表扫描。优化方法:可以在c_str_value列上加上索引。

    mysql> EXPLAIN SELECT * FROM t1 where t1.c_str_value='1111';
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL | 4915 | Using where |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    
  2. 查询一个表的所有记录,如下。优化方法:在满足业务需求的情况下,把查询的所有列改成某几列,这样若是某个索引数据满足条件的话,可以不用遍历全表,而仅仅遍历索引数据即可。

    mysql> EXPLAIN SELECT * FROM t1;
    +----+-------------+-------+------+---------------+------+---------+------+------+-------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------+
    |  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL | 4915 | NULL  |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------+
    

    最慢的查询,必须得review这些SQL,数据量大的情况下,必然带来灾难!

总结

通过以上,我们可以看到效率排序为: const < eq_ref < ref < ref_or_null(range) < index < ALL,通常index和ALL是需要重点注意的。让我们的嗅觉灵敏起来吧。:)

本着理论指导实践的原则,以上用实例对理论做了实践,难免出错,敬请指正。

遗留问题:

  1. unique index如何保存NULL的索引?这个key允许多条NULL记录存在么?
  2. primary index可以为NULL么?
  3. index列中如何存储NULL数据?

转载于:https://www.cnblogs.com/figo-cui/p/5177780.html

SQL优化:使用explain相关推荐

  1. SQL 优化工具 Explain

    ## SQL优化工具Explain MySQL是关系型数据库中的一种,**查询功能强,数据一致性高,数据安全性高,支持二级索引**.但性能方面稍逊与非关系型数据库,特别是百万级别以上的数据,很容易出现 ...

  2. sql优化之explain关键字分析

      explain是非常重要的关键字,要善于运用它.  通过explain我们可以获得以下信息: 表的读取顺序 数据读取操作的操作类型 哪些索引可以使用 哪些索引被实际使用 表之间的引用 每张表有多少 ...

  3. 基于explain分析进行SQL优化(实例分析)

    背景 最近遇到一个问题,实现相同功能的几个 sql 不知道哪个最优.当然直接测试可以得到最终结论,但是还是不知道为啥慢,为啥快,还可以怎么样再进行优化?这些问题没有解决.下面通过一个实例来介绍下 sq ...

  4. 面试官:不会看 Explain执行计划,简历敢写 SQL 优化?

    来自:程序员内点事 昨天中午在食堂,和部门的技术大牛们坐在一桌吃饭,作为一个卑微技术渣仔默默的吃着饭,听大佬们高谈阔论,研究各种高端技术,我TM也想说话可实在插不上嘴. 聊着聊着突然说到他上午面试了一 ...

  5. map语法获取index_MySQL SQL语法优化——使用Explain查看执行计划

    夜深,最近在写SQL优化的一些文章,看到私聊中,有很多人在问如何判断是否需要优化或者是如何查看MySQL执行计划,本文简要介绍一下MySQL EXPLAIN命令. EXPLAIN命令是查看优化器如何决 ...

  6. MySQL02--高级(BTreeB+Tree、聚簇索引非聚簇索引、性能分析(Explain)、索引、sql优化)

    1.MySQL架构 2.sql 执行顺序: FROM <LEFT_TABLE> ON <JOIN_CONDITION> <JOIN_TYPR> JOIN <R ...

  7. MySQL数据库性能优化由浅入深(表设计、慢查询、SQL索引优化、Explain分析、Show Profile分析、配置优化)

    文章目录 0 SQL性能分析 1 表的设计合理化 1.1 为什么需要范式 1.2 三范式原理 1.3 什么样的表才满足三范式 2 慢查询 2.1 慢查询介绍 2.2 慢查询步骤 3 添加适当索引 3. ...

  8. SQL索引优化之explain查询计划

    前言 最近工作当中使用SQL的场景比较多,遇到了一些瓶颈,在SQL优化这方面做了一些了解. 在SQL应用中影响性能最多的就是慢查询,关于慢查询的优化,主要有这几个着手的方向: 监控sql执行情况,发邮 ...

  9. 中秋节,送上一次非常有趣的SQL优化实战经历

    点击上方"搜云库技术团队",选择"设为星标" 回复"1024"或"面试题"获取4T学习资料 补充:看到好多朋友后台留言说 ...

  10. [转]Mysql中的SQL优化与执行计划

    From : http://religiose.iteye.com/blog/1685537 一,如何判断SQL的执行效率? 通过explain 关键字分析效率低的SQL执行计划. 比如: expla ...

最新文章

  1. 搭建免费ftp服务,视频演示
  2. 微软将发布5月安全漏洞补丁 修补PPT
  3. typora打开pdf文件提示文件过大_Win7/Win10拷贝到U盘容量足够却提示文件过大的解决方法...
  4. 大学生转行IT,零基础非计算机专业可以学会吗?
  5. python编程(gevent入门)
  6. quartus仿真17:T触发器的时序逻辑电路
  7. 陈国荣 计算机科学,信息工程中计算机网络技术的应用-网络技术论文-计算机论文(10页)-原创力文档...
  8. python运维主要学什么_python运维入门该学什么
  9. Javascript 调用MSAgent(调用office助手显示动画)
  10. 视频直播卡顿分析及优化
  11. 【交换机在江湖】第十章 接口配置锦囊妙计之二----端口隔离
  12. xftp中文文件名乱码
  13. GET 和 POST 有什么区别?
  14. 华泰证券人工智能系列(1):人工智能选股框架及经典算法简介
  15. .vimrc快捷键设置
  16. 微信小程序开发笔记二(WXSS和CSS样式美化)
  17. LeetCode刷题之---上一个排序
  18. 快速提升网站排名_使用快排优化的方法
  19. 没有gpedit.msc这个文件获取管理员权限
  20. B2B实现支付分账的简单方法

热门文章

  1. mysql查看连接数 (linux)
  2. oracle错误:ORA-12545
  3. 教程:从FLASH过渡到SILVERLIGHT.(实例+源码)
  4. 3.11上午课程重点
  5. Linux DNS (2)搭建DNS服务器
  6. 实现不同域(Domain)之间的数据交换(转)
  7. 验证ArrayList插入同样数据使用指定容量和默认容量的效率
  8. P1096 $Hanoi$双塔问题
  9. vue vuex vue-router后台项目——权限路由(超详细简单版)
  10. Linux之SELinux的基本应用