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数据?
SQL优化:使用explain的更多相关文章
- 浅谈SQL优化入门:2、等值连接和EXPLAIN(MySQL)
1.等值连接:显性连接和隐性连接 在<MySQL必知必会>中对于等值连接有提到两种方式,第一种是直接在WHERE子句中规定如何关联即可,那么第二种则是使用INNER JOIN关键字.如下例 ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(十二)数据层优化-explain关键字及慢sql优化
本文提要 从编码角度来优化数据层的话,我首先会去查一下项目中运行的sql语句,定位到瓶颈是否出现在这里,首先去优化sql语句,而慢sql就是其中的主要优化对象,对于慢sql,顾名思义就是花费较多执行时 ...
- SQL优化 MySQL版 -分析explain SQL执行计划与笛卡尔积
SQL优化 MySQL版 -分析explain SQL执行计划 作者 Stanley 罗昊 [转载请注明出处和署名,谢谢!] 首先我们先创建一个数据库,数据库中分别写三张表来存储数据; course: ...
- mysql 开发进阶篇系列 2 SQL优化(explain分析)
接着上一篇sql优化来说 1. 定位执行效率较低的sql 语句 通过两种方式可以定位出效率较低的sql 语句. (1) 通过上篇讲的慢日志定位,在mysqld里写一个包含所有执行时间超过 long_q ...
- SQL优化笔记一:索引和explain
目录 为什么需要优化SQL SQL优化的重点 索引 索引的结构 索引的优缺点总结: 索引的分类 索引操作 B树 实战 问题 数据库方面,我会使用MySQL来讲解 为什么需要优化SQL 性能低,执行时间 ...
- Explain 执行计划 和 SQL优化
Explain 介绍 在分析查询性能时,考虑EXPLAIN关键字同样很管用.EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作.以及MySQL成功返回结果集需要 ...
- EXPLAIN sql优化方法(2) Using temporary ; Using filesort
优化GROUP BY语句 默认情况下,MySQL对所有GROUP BY col1,col2...的字段进行排序.这与在查询中指定ORDER BY col1,col2...类似.因此,如果显式包括一 ...
- SQL优化(三)—— 索引、explain分析
SQL优化(三)—— 索引.explain分析 一.什么是索引 索引是一种排好序的快速查找的数据结构,它帮助数据库高效的查询数据 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据 ...
- SQL优化之慢查询和explain以及性能分析
性能优化的思路 首先需要使用慢查询功能,去获取所有查询时间比较长的SQL语句 使用explain去查看该sql的执行计划 使用show profile去查看该sql执行时的性能问题 MySQL性能优化 ...
- 不会看 Explain执行计划,劝你简历别写熟悉 SQL优化
昨天中午在食堂,和部门的技术大牛们坐在一桌吃饭,作为一个卑微技术渣仔默默的吃着饭,听大佬们高谈阔论,研究各种高端技术,我TM也想说话可实在插不上嘴. 聊着聊着突然说到他上午面试了一个工作6年的程序员, ...
随机推荐
- PAT 1110 Complete Binary Tree[判断完全二叉树]
1110 Complete Binary Tree(25 分) Given a tree, you are supposed to tell if it is a complete binary tr ...
- 排序问题Java
package zhuzhuangtu; import java.util.*; import java.io.*; public class Main{ public static void mai ...
- cocos代码研究(19)Widget子类ImageView学习笔记
理论基础 显示图片的小控件,继承自 Widget . 代码实践 static ImageView * create()创建一个空的ImageView static ImageView * create ...
- 11月26号host
127.0.0.1 localhost255.255.255.255 broadcasthost::1 localhostfe80::1%lo0 localhost # Google start216 ...
- hdu1199 线段树
这题说的是给了 n 个操作. 每个操作会把 [a,b] 之间的球 涂为黑色或者 白色, 然后最后问 最长的连续的白色的 球有多少个,初始的时候全是黑的. 我们将所有的点离散化, 记得离散 a-1, b ...
- Android ActionBar自定义
关于自定义的ActionBar的实现过程,这里做下笔记以供之后查看. 1.默认状态 使用Android Studio新建一个名为“ActionBar”的应用,默认actionbar如图(1)所示. 图 ...
- LoRa无线通信设计(一)原理
LoRa无线通信设计(一)原理 引言 1901年,古列尔默.马可尼把长波无线电信号从Cornwall(康沃尔,位于英国的西南部)跨过大西洋传送到3200公里之外的Newfoundland(加拿大的纽芬 ...
- 【转载】package-info
本文是转载,原文地址:http://strong-life-126-com.iteye.com/blog/806246 package-info.java对于经常使用外部包的程序员来说应该是熟悉陌生人 ...
- 在apache中使用.htaccess文件的注意事项
在apache的配置文件中: <VirtualHost *:80> ServerName tp5.com DocumentRoot d:/wamp/www/tp5.com/public & ...
- nw.js node-webkit系列(18)怎么对.js进行编译以防你的代码暴露出来
原文链接:http://blog.csdn.net/zeping891103/article/details/50819102 参考:https://segmentfault.com/a/119000 ...