EXPLAIN sql优化方法(1) 添加索引
假设我们有两个数据表t1和t2,每个有1000行,包含的值从1到1000。下面的查询查找出两个表中值相同的数据行:
- mysql> SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2;
- +------+------+
- | i1 | i2 |
- +------+------+
- | 1 | 1 |
- | 2 | 2 |
- | 3 | 3 |
- | 4 | 4 |
- | 5 | 5 |
mysql> SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2;
+------+------+
| i1 | i2 |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
两个表都没有索引的时候,EXPLAIN产生下面的结果:
- mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
- *************************** 1. row ***************************
- id: 1
- select_type: SIMPLE
- table: t1
- type: ALL
- possible_keys: NULL
- key: NULL
- key_len: NULL
- ref: NULL
- rows: 1000
- Extra:
- *************************** 2. row ***************************
- id: 1
- select_type: SIMPLE
- table: t2
- type: ALL
- possible_keys: NULL
- key: NULL
- key_len: NULL
- ref: NULL
- rows: 1000
- Extra: Using whe
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra: Using whe
类型列中的ALL表明要进行检查所有数据行的全表扫描。可能键列中的NULL表明没有找到用于提高查询速度的备选索引(键、键长度和参考列都是NULL也是因为缺少合适的索引)。Using where表明使用WHERE子句中的信息来识别合格的数据行。 这段信息告诉我们,优化器没有为提高执行查询的效率找到任何有用的信息:
它将对t1表进行全表扫描。
对于t1中的每一行,它将执行t2的全表扫描,使用WHERE子句中的信息识别出合格的行。
行数值显示了优化器估计的每个阶段查询需要检查的行数。T1的估计值是1000,因为1000可以完成全表扫描。相似地,t2的估计值也是1000,但是这个值是对于t1的每一行的。换句话说,优化器所估计的处理该查询所需要检查的数据行组合的数量是1000×1000,也就是一百万。这会造成很大的浪费 ,因为实际上只有1000个组合符合WHERE子句的条件。
为了使这个查询的效率更高,给其中一个联结列添加索引 并重新执行EXPLAIN语句:
- mysql> ALTER TABLE t2 ADD INDEX (i2);
- mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
- *************************** 1. row ***************************
- id: 1
- select_type: SIMPLE
- table: t1
- type: ALL
- possible_keys: NULL
- key: NULL
- key_len: NULL
- ref: NULL
- rows: 1000
- Extra:
- *************************** 2. row ***************************
- id: 1
- select_type: SIMPLE
- table: t2
- type: ref
- possible_keys: i2
- key: i2
- key_len: 5
- ref: sampdb.t1.i1
- rows: 10
- Extra: Using where; Using index
mysql> ALTER TABLE t2 ADD INDEX (i2);
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 10
Extra: Using where; Using index
我们可以看到性能提高了。T1的输出没有改变(表明还是需要进行全表扫描),但是优化器处理t2的方式就有所不同了:
类型从ALL改变为ref,意味着可以使用参考值(来自t1的值)来执行索引查找,定位t2中合格的数据行。
参考值在参考(ref)字段中给出了:sampdb.t1.i1。
数值从1000降低到了10,显示出优化器相信对于t1中的每一行,它只需要检查t2中的10行(这是一个悲观的估计值。实际上,在t2中只有一行与 t1中数据行匹配。我们在后面会看到如何帮助优化器改善这个估计值)。数据行组合的全部估计值使1000×10=10000。它比前面的没有索引的时候估 计出来的一百万好多了。
对t1进行索引有价值吗?实际上,对于这个特定的联结操作,扫描一张表是必要的,因此没有必要对t1建立索引。如果你想看到效果,可以索引t1.i1并再次运行EXPLAIN:
- mysql> ALTER TABLE t1 ADD INDEX (i1);
- mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
- *************************** 1. row ***************************
- id: 1
- select_type: SIMPLE
- table: t1
- type: index
- possible_keys: i1
- key: i1
- key_len: 5
- ref: NULL
- rows: 1000
- Extra: Using index
- *************************** 2. row ***************************
- id: 1
- select_type: SIMPLE
- table: t2
- type: ref
- possible_keys: i2
- key: i2
- key_len: 5
- ref: sampdb.t1.i1
- rows: 10
- Extra: Using where; Using index
mysql> ALTER TABLE t1 ADD INDEX (i1);
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: i1
key: i1
key_len: 5
ref: NULL
rows: 1000
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 10
Extra: Using where; Using index
上面的输出与前面的EXPLAIN的输出相似,但是添加索引对t1的输出有一些改变。类型从NULL改成了index,附加(Extra)从空的改成了 Using index。这些改变表明,尽管对索引的值仍然需要执行全表扫描,但是优化器还是可以直接从索引文件中读取值,根据不需要使用数据文件。你可以从 MyISAM表中看到这类结果,在这种情况下,优化器知道自己只询问索引文件就能够得到所有需要的信息。对于InnoDB 和BDB表也有这样的结果,在这种情况下优化器可以单独使用索引中的信息而不用搜索数据行。
我们可以运行ANALYZE TABLE使优化器进一步优化估计值。这会引起服务器生成键值的静态分布。分析上面的表并再次运行EXPLAIN得到了更好的估计值:
- mysql> ANALYZE TABLE t1, t2;
- mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
- *************************** 1. row ***************************
- id: 1
- select_type: SIMPLE
- table: t1
- type: index
- possible_keys: i1
- key: i1
- key_len: 5
- ref: NULL
- rows: 1000
- Extra: Using index
- *************************** 2. row ***************************
- id: 1
- select_type: SIMPLE
- table: t2
- type: ref
- possible_keys: i2
- key: i2
- key_len: 5
- ref: sampdb.t1.i1
- rows: 1
- Extra: Using where; Using inde
mysql> ANALYZE TABLE t1, t2;
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: i1
key: i1
key_len: 5
ref: NULL
rows: 1000
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 1
Extra: Using where; Using inde
在这种情况下,优化器估计在t2中与t1的每个值匹配的数据行只有一个。
- mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B where A . catid = B . id ;
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
- | 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 46585 | |
- | 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
- 2 rows in set ( 0.00 sec )
mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B where A . catid = B . id ;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
| 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 46585 | |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
2 rows in set ( 0.00 sec )
这个是我们经常使用的一种查询方式,对B表的联接类型使用了eq_ref,索引使用了PRIMARY,但是对于A表,却没有使用任何索引,这可能不是我们想要的。
查看以上SQL语句,我们可能会想到,有必要给A.catid加个索引了。
- mysql > alter table jos_content add index idx_catid ( ` catid ` ) ;
- Query OK , 46585 rows affected ( 0.75 sec )
- Records : 46585 Duplicates : 0 Warnings : 0
- mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B where A . catid = B . id ;
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
- | 1 | SIMPLE | A | ALL | idx_catid | NULL | NULL | NULL | 46585 | |
- | 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
- 2 rows in set ( 0.00 sec )
mysql > alter table jos_content add index idx_catid ( ` catid ` ) ;
Query OK , 46585 rows affected ( 0.75 sec )
Records : 46585 Duplicates : 0 Warnings : 0 mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B where A . catid = B . id ;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
| 1 | SIMPLE | A | ALL | idx_catid | NULL | NULL | NULL | 46585 | |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
2 rows in set ( 0.00 sec )
- mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B . id and A . sectionid = C . id ;
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
- | 1 | SIMPLE | C | index | PRIMARY | PRIMARY | 4 | NULL | 2 | Using index |
- | 1 | SIMPLE | A | ALL | idx_catid | NULL | NULL | NULL | 46585 | Using where ; Using join buffer |
- | 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
- +----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
- 3 rows in set ( 0.00 sec )
mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B . id and A . sectionid = C . id ;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
| 1 | SIMPLE | C | index | PRIMARY | PRIMARY | 4 | NULL | 2 | Using index |
| 1 | SIMPLE | A | ALL | idx_catid | NULL | NULL | NULL | 46585 | Using where ; Using join buffer |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
3 rows in set ( 0.00 sec )
这里显示了Mysql先将C表读入查询,并使用PRIMARY索引,然后联合A表进行查询,这时候type显示的是ALL,可以用的索引有idx_catid,但是实际没有用。
原因非常明显,因为使用的连接条件是A.sectionid=C.id,所以我们给A.sectionid加个索引先。
- mysql > alter table jos_content add index idx_section ( ` sectionid ` ) ;
- Query OK , 46585 rows affected ( 0.89 sec )
- Records : 46585 Duplicates : 0 Warnings : 0
- mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B . id and A . sectionid = C . id ;
- +----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
- | 1 | SIMPLE | C | index | PRIMARY | PRIMARY | 4 | NULL | 2 | Using index |
- | 1 | SIMPLE | A | ref | idx_catid , idx_section | idx_section | 4 | joomla_test . C . id | 23293 | Using where |
- | 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
- +----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
- 3 rows in set ( 0.00 sec )
mysql > alter table jos_content add index idx_section ( ` sectionid ` ) ;
Query OK , 46585 rows affected ( 0.89 sec )
Records : 46585 Duplicates : 0 Warnings : 0 mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B . id and A . sectionid = C . id ;
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| 1 | SIMPLE | C | index | PRIMARY | PRIMARY | 4 | NULL | 2 | Using index |
| 1 | SIMPLE | A | ref | idx_catid , idx_section | idx_section | 4 | joomla_test . C . id | 23293 | Using where |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | Using where |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
3 rows in set ( 0.00 sec )
这时候显示结果告诉我们,效果很明显,在连接A表时type变成了ref,索引使用了idx_section,如果我们注意看后两列,对A表的查询结果后一次明显少了一半左右,而且没有用到join buffer。
这个表读入的顺序是Mysql优化器帮我们做的,可以得知,用记录数少的表做为基础表进行联合,将会得到更高的效率。
- mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . id left join jos_sections C on A . sectionid = C . id ;
- +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
- | 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 46585 | |
- | 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | |
- | 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . sectionid | 1 | Using index |
- +----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
- 3 rows in set ( 0.00 sec )
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . id left join jos_sections C on A . sectionid = C . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
| 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 46585 | |
| 1 | SIMPLE | B | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . catid | 1 | |
| 1 | SIMPLE | C | eq_ref | PRIMARY | PRIMARY | 4 | joomla_test . A . sectionid | 1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
3 rows in set ( 0.00 sec )
Mysql读入表的顺序被改变了,这意味着,如果我们用left join来做连接查询,Mysql会按SQL语句中表出现的顺序读入,还有一个有变化的地方是联接B和C的type都变成了eq_ref,前边我们说过, 这样说明Mysql可以找到唯一的行,这个效率是比ref要高的。
原文地址:http://hudeyong926.iteye.com/blog/784663
EXPLAIN sql优化方法(1) 添加索引的更多相关文章
- EXPLAIN sql优化方法(2) Using temporary ; Using filesort
优化GROUP BY语句 默认情况下,MySQL对所有GROUP BY col1,col2...的字段进行排序.这与在查询中指定ORDER BY col1,col2...类似.因此,如果显式包括一 ...
- EXPLAIN sql优化方法(3)DERIVED
派生表和视图的性能 从MySQL 4.1开始,它已经支持派生表.联机视图或者基本的FROM从句的子查询. 这些特性之间彼此相关,但是它们之间的性能比较如何呢? MySQL 5.0 中的派生表似乎和视图 ...
- SQL优化(三)—— 索引、explain分析
SQL优化(三)—— 索引.explain分析 一.什么是索引 索引是一种排好序的快速查找的数据结构,它帮助数据库高效的查询数据 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据 ...
- DB-SQL-MySQL-杂项-调优:Mysql千万以上数据优化、SQL优化方法
ylbtech-DB-SQL-MySQL-杂项-调优:Mysql千万以上数据优化.SQL优化方法 1.返回顶部 1. 1,单库表别太多,一般保持在200以下为宜 2,尽量避免SQL中出现运算,例如se ...
- mysql索引sql优化方法、步骤和经验
MySQL索引原理及慢查询优化 http://blog.jobbole.com/86594/ 细说mysql索引 https://www.cnblogs.com/chenshishuo/p/50300 ...
- SQL优化笔记一:索引和explain
目录 为什么需要优化SQL SQL优化的重点 索引 索引的结构 索引的优缺点总结: 索引的分类 索引操作 B树 实战 问题 数据库方面,我会使用MySQL来讲解 为什么需要优化SQL 性能低,执行时间 ...
- OGG复制进程延迟高,优化方法二(存在索引),SQL选择不好的索引
https://www.cnblogs.com/lvcha001/p/13469500.html 接前序,本次场景中有索引,但是OGG复制进程使用了低效率的索引? 类似SQL使用低效索引,如何让Or ...
- sql优化方法学习和总结
首先要问自己几个问题: 哪些类型的sql会散发出坏味道? sql优化的基本原理是什么,为什么有的sql快有的慢? sql优化和底层的存储引擎关系大么? 怎么看执行过程? 优化建议 1. 缓存查询,sq ...
- SQL优化 MySQL版 - 避免索引失效原则(二)
避免索引失效原则(二) 注:继上一篇文章继续讲解: 避免索引失效原则(一)https://www.cnblogs.com/StanleyBlogs/p/10482048.html#4195062 作者 ...
随机推荐
- js的内建arguments数组
调用函数时,只需在函数名后加一对用于传递参数的括号即可.var result = sum(1,2) 如果调用参数(a,b)的时候没有给值,则值默认为undefined.即使传递参数过多,多余的部分也会 ...
- Realm Update failed - Android
Realm Update failed - Android Ask Question up vote 0 down vote favorite I'm using realm for my andro ...
- B1003 物流运输(最短路 + dp)
这个dp其实不是那么难,状态其实很好想,但是细节有少许偏差. 当时我并没有想到最短路要在dp之外写,后来看题解之后发现要预处理出来每段时间1~M的最短路,然后直接dp. 题目: Description ...
- 85.Ext.Window
转自:https://chenjumin.iteye.com/blog/668421 1.主要配置项: closable:是否允许关闭窗口,默认为true. closeActi ...
- python 11:range(起始索引,终止索引,步数)(默认情况下步数为1,生成从起始索引,每次增加(终止索引-起始索引)/步数,到终止索引前的数字串)
squares = [] for value in range(1,11): #第三参数默认为1,生成从1开始,每次增加1步数,到11前的10为止的数字串 square = value ** 2 sq ...
- EditPlus 1:更改默认编码方式
打开软件点击上面的菜单栏Tools(工具),再找到Configure User Tools(用户配置工具)点击,再找到左边栏File点击,这个时候可以看到右边栏的Default encoding点击可 ...
- mybatis parameterType报错:There is no getter for property named 'xxx' in 'class java.lang.String'
方法1: 当parameterType = "java.lang.String" 的时候,参数读取的时候必须为 _parameter 方法2: 在dao层的时候,设置一下参数,此方 ...
- Safe Area Layout Guide before iOS 9.0
今天使用Xcode9.1重建项目,什么都没写运行报错:Safe Area Layout Guide before iOS 9.0!目前为止,不晓得原因,现记录解决方法:
- JWPL工具处理维基百科wikipedia数据用于NLP
JWPL处理维基百科数据用于NLP 处理zhwiki JWPL是一个Wikipedia处理工具,主要功能是将Wikipedia dump的文件经过处理.优化导入mysql数据库,用于NLP过程.以下以 ...
- [ller必读] LoveLive! 必备技能之 Python Pillow 自动处理截图
起因 喜欢的歌,静静地听:喜欢的人,远远的看.30天前,就是3月14号,我情不自禁地走近了<LoveLive!学院偶像祭>,这是我的第一张卡片(见下图).第二天也就是3月15日,海未生日了 ...