SQL优化:使用explain
前文说了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
使用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 | +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
使用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 | +----+-------------+-------+-------+---------------+--------------+---------+-------+------+-------+
使用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 | +----+-------------+-------+------+---------------+--------------+---------+-------+------+-----------------------+
使用非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 | +----+-------------+-------+-------+-----------------------+-----------------------+---------+-------+------+-------+
使用非唯一的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)。
使用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。
使用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)。
使用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 | +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------+
多表关联时使用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 | +----+-------------+-------+------+---------------+-------+---------+------------------------+------+-------------+
匹配左侧前缀(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 | +----+-------------+-------+------+-------------------+-------------------+---------+-----------------+------+-------------+
使用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的记录。
使用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 | +----+-------------+-------+-------------+---------------+-------+---------+-------+------+-----------------------+
其余不再给输出,参照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()等操作符。
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 | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
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要高。分为两种情况:
索引数据足够满足要求,即索引数据包括了查询的所有数据(在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 | +----+-------------+-------+-------+---------------+-------+---------+------+------+-------------+
需要根据某个索引列的顺序进行查询,如下。第一条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
终于到了最差的情况,全表扫描。即没有合适的索引数据,所以只能一行一行的扫描数据了,沦落至此,可想而知效果极差。例如:
查询符合条件的记录,如下,由于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 | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
查询一个表的所有记录,如下。优化方法:在满足业务需求的情况下,把查询的所有列改成某几列,这样若是某个索引数据满足条件的话,可以不用遍历全表,而仅仅遍历索引数据即可。
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是需要重点注意的。让我们的嗅觉灵敏起来吧。:)
本着理论指导实践的原则,以上用实例对理论做了实践,难免出错,敬请指正。
遗留问题:
- unique index如何保存NULL的索引?这个key允许多条NULL记录存在么?
- primary index可以为NULL么?
- index列中如何存储NULL数据?
转载于:https://www.cnblogs.com/figo-cui/p/5177780.html
SQL优化:使用explain相关推荐
- SQL 优化工具 Explain
## SQL优化工具Explain MySQL是关系型数据库中的一种,**查询功能强,数据一致性高,数据安全性高,支持二级索引**.但性能方面稍逊与非关系型数据库,特别是百万级别以上的数据,很容易出现 ...
- sql优化之explain关键字分析
explain是非常重要的关键字,要善于运用它. 通过explain我们可以获得以下信息: 表的读取顺序 数据读取操作的操作类型 哪些索引可以使用 哪些索引被实际使用 表之间的引用 每张表有多少 ...
- 基于explain分析进行SQL优化(实例分析)
背景 最近遇到一个问题,实现相同功能的几个 sql 不知道哪个最优.当然直接测试可以得到最终结论,但是还是不知道为啥慢,为啥快,还可以怎么样再进行优化?这些问题没有解决.下面通过一个实例来介绍下 sq ...
- 面试官:不会看 Explain执行计划,简历敢写 SQL 优化?
来自:程序员内点事 昨天中午在食堂,和部门的技术大牛们坐在一桌吃饭,作为一个卑微技术渣仔默默的吃着饭,听大佬们高谈阔论,研究各种高端技术,我TM也想说话可实在插不上嘴. 聊着聊着突然说到他上午面试了一 ...
- map语法获取index_MySQL SQL语法优化——使用Explain查看执行计划
夜深,最近在写SQL优化的一些文章,看到私聊中,有很多人在问如何判断是否需要优化或者是如何查看MySQL执行计划,本文简要介绍一下MySQL EXPLAIN命令. EXPLAIN命令是查看优化器如何决 ...
- MySQL02--高级(BTreeB+Tree、聚簇索引非聚簇索引、性能分析(Explain)、索引、sql优化)
1.MySQL架构 2.sql 执行顺序: FROM <LEFT_TABLE> ON <JOIN_CONDITION> <JOIN_TYPR> JOIN <R ...
- MySQL数据库性能优化由浅入深(表设计、慢查询、SQL索引优化、Explain分析、Show Profile分析、配置优化)
文章目录 0 SQL性能分析 1 表的设计合理化 1.1 为什么需要范式 1.2 三范式原理 1.3 什么样的表才满足三范式 2 慢查询 2.1 慢查询介绍 2.2 慢查询步骤 3 添加适当索引 3. ...
- SQL索引优化之explain查询计划
前言 最近工作当中使用SQL的场景比较多,遇到了一些瓶颈,在SQL优化这方面做了一些了解. 在SQL应用中影响性能最多的就是慢查询,关于慢查询的优化,主要有这几个着手的方向: 监控sql执行情况,发邮 ...
- 中秋节,送上一次非常有趣的SQL优化实战经历
点击上方"搜云库技术团队",选择"设为星标" 回复"1024"或"面试题"获取4T学习资料 补充:看到好多朋友后台留言说 ...
- [转]Mysql中的SQL优化与执行计划
From : http://religiose.iteye.com/blog/1685537 一,如何判断SQL的执行效率? 通过explain 关键字分析效率低的SQL执行计划. 比如: expla ...
最新文章
- 搭建免费ftp服务,视频演示
- 微软将发布5月安全漏洞补丁 修补PPT
- typora打开pdf文件提示文件过大_Win7/Win10拷贝到U盘容量足够却提示文件过大的解决方法...
- 大学生转行IT,零基础非计算机专业可以学会吗?
- python编程(gevent入门)
- quartus仿真17:T触发器的时序逻辑电路
- 陈国荣 计算机科学,信息工程中计算机网络技术的应用-网络技术论文-计算机论文(10页)-原创力文档...
- python运维主要学什么_python运维入门该学什么
- Javascript 调用MSAgent(调用office助手显示动画)
- 视频直播卡顿分析及优化
- 【交换机在江湖】第十章 接口配置锦囊妙计之二----端口隔离
- xftp中文文件名乱码
- GET 和 POST 有什么区别?
- 华泰证券人工智能系列(1):人工智能选股框架及经典算法简介
- .vimrc快捷键设置
- 微信小程序开发笔记二(WXSS和CSS样式美化)
- LeetCode刷题之---上一个排序
- 快速提升网站排名_使用快排优化的方法
- 没有gpedit.msc这个文件获取管理员权限
- B2B实现支付分账的简单方法