explain 执行计划
explain + sql 语句
explain + select * from tbl_user 横向列表展示
explain + select * from tbl_user \G 纵向列表展示
作用:1、表的读取顺序
2、数据读取操作的操作类型
3、那些索引可以使用
4、那些索引被实际使用
5、表之间的引用
6、每张表有多少行被优化器查询
id 1-select 查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
2-三种情况 这里的id 说的是下面select_type对应的6种类型的数值
2.1=id相同,执行顺序由上至下(sql比如是关联查询)
2.2=id不同,(子查询)id的序号会递增,id值越大优先级越高
2.3=id相同不同,
select_type select 关键字对应的那个查询类型
1.SIMPLE 简单的selet 查询,查询中不包含子查询或UNION
2.PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为
3.SUBQUERY 在select或WHERE列表中包含了子查询
4.DERIVED 在FROM类表中包含的子查询被标记为DERIVED(衍生)
MySQL会递归执行这些子查询,把结果放在临时表里
5.UNION 若第二个SELECT出现在UNION之后,则被标记为UNION
若UNION包含在FROM子句的子查询中,外层SELECT将被标记为DERIVED
6.UNION RESULT 从UNION表获取结果的SELECT
table 显示这一行数据时关于那张表的
type ** 访问类型
ALL index range ref eq_ref const system NULL
从最好到最差
system > const > eq_ref > ref > range > index > ALL
system :表只有一条记录(等于系统表),这是const类型的特例,平时不会出现,这个可以忽略不计
const :表示通过索引一次就可以找到了,const用于比较primary key 或者unique索引,因为匹配一行数据
所以很快,如将主键至于where列表中,MySQL就能将该查询转换为一个常量
eq_ref :唯一索引扫描,对于每个索引建,表中只有一条记录与之匹配,常见于主键或唯一索引扫描
ref :非唯一索引扫描,返回匹配某个单独值得所有行
本质上也是一种索引访问,它返回所有匹配某个单独值得行,然而,
它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
range :只检索给定范围的行,使用一个索引来选择行,key列显示使用了那个索引
一般就是在你的where语句中出现了between、<、>、in等的查询
这种范围扫描索引扫描比全表扫描要好,以为它只需要开始于索引的某一个点,
而结束于另一点,不用扫描全部索引
index :Full Index Scan,Index于All区别为index类型值遍历索引树,这通常比ALL快,因为索引文件通常比
数据文件小,(也就是说虽然all和index都是读全表)但index是从索引中读取的
而all是从硬盘中读的
possible_keys ** 可能用到的索引
1.显示可能应用在这张表中的索引,一个或多个
2.查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
key ** 实际用到的索引
1.实际使用的索引,如果为NULL,则没有使用索引
2.查询中若使用了覆盖索引,则该索引仅出现在key列表中
key_len
1.表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好
2.ken_len的值为索引字段的最大可能长度,并非实际使用长度,即ken_len是根据表定义计算而得,不是通过表内检索 出的
ref 显示索引的那一列被使用了,如果可能的话,是一个常数,那些列或常量被用于查找索引列上的值
rows 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
Extra 包含不适合在他列中显示但十分重要的额外信息
1.Using filesort :说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取
(不好) MySQL中无法利用索引完成的排序操作成为“文件排序”
2.Using tempporay : 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表,常见于排序order by 和分组查询group by.
(不好)
3.USING index :表示相应的select操作中使用了覆盖索引(Covering index) ,避免访问了表的数据行,效率不错
如果同时出现using where ,表明索引被用来执行索引键值的查找
如果没有同时出现using where ,表明索引用来读取数据而非执行查找动作
覆盖索引(Covering Index) :
方式一
就是select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,
而不必根据索引再次读取数据文件,换句话说,查询列要被建的索引覆盖
** 如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *
因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降
4.Using where 表明使用了where 过滤
5.using join buffer 使用了连接缓存(可以调大join_buffer)
6.impossible where where 子句的值总是false,不能用来获取任何元素
7.select tables optimized away 没有group by 子句的情况下,基于索引优化MIN/MAX操作或者
对于MyISAM存储引擎优化Count(*)操作,不必等到执行阶段在进行计算
查询执行计划生成阶段即完成优化
8.distinct 在找到第一个匹配的元祖后即停止找同样值得操作
开始优化
CREATE TABLE IF NOT EXISTS 'article'(
id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
author_id INT(10) UNSIGNED NOT NULL,
category_id INT(10) UNSIGNED NOT NULL,
views INT(10) UNSIGNED NOT NULL,
comments INT(10) UNSIGNED NOT NULL,
title VARBINARY(255) NOT NULL,
content TEXT NOT NULL,
)
INSERT INTO article(author_id,category_id,views,comments,title,content) VALUES
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3'),
案例一、
##查询category_id 为1 且comments大于1的情况下,views最多的article_id
EXPLAIN SELECT id,author_id, FROM article WHERE category_id=1 AND comment > 1 ORDER BY views desc LIMIT 1;
结论:很显然,type是ALL,即最坏的情况,Extra是还是出现了 Using filesort ,也是最坏的情况,优化是必须的
1.1 新建索引+删除索引
ALTER TABLE 'acticle' ADD INDEX idx_article_ccv on ('category_id','comment','viesw');
create index idx_arcticle_ccv on article(category_id,comment,views);
执行案例1的sql
DROP INDEX idx_article_ccv ON article;
1.2 第2次EXPLAIN
EXPLAIN SELECT id,author_id FROM article where category_id=1 AND comment > 1 ORDERY BY views desc LIMIT 1;
EXPLAIN SELECT id,author_id FROM article where category_id=1 AND comment = 3 ORDERY BY views desc LIMIT 1;
结论;
type 变成了range,这是可以忍受的,但是extra里使用了 Using filesort仍是无法接受的
但是我们已经建立了索引,为啥没有用?
这是因为按照BTree索引的工作原理,先排序,category_id,
如果遇到相同的category_id则在排序,comments如果遇到comments则再排序views,
当comments字段在联合索引里处于中间位置时,因comments > 1 条件是一个范围(所谓range)
MySQL 无法利用索引在对后面的views部分进行检索,即range类型查询字段后面的索引无效。
1.3删除第一次建立的索引
DROP INDEX idx_article_ccv ON article;
1.4第2次新建索引
ALTER TABLE 'article' ADD INDEX idx_article_ccv('category_id','views');
create index idx_article on article(category_id,views);
1.5 第3次EXPLAIN
EXPLAIN SELECT id,author_id FROM article WHERE category_id=1 AND comment >1 ORDER BY views DESC LIMIT 1;
结论:可以看到,type变成了ref,Extra中的Using filesort也消失了,结果非常理想
DROP INDEX idx_article_cv ON article;
案例二、两个表
CREATE TABLE NOT EXISTS class(
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
card_INT(10) UNSIGNED NOT NULL,
PRIMARY KEY ID
)
CREATE TABLE IF NOT EXSITS book(
bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
card INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(bookid)
)
INSERT INTO class(card) VALUES(floor(1+(RAND()*20)));
INSERT INTO book(card) VALUES(floor(1+(RAND()*20)));
分别插入20条数据
第一次Explain
Explain SELECT * FROM class LEFT JOIN book ON class.card= book.card
#结论:type 有All
添加索引优化
ALTER TABLE book Add iNDEX Y(card)
第二次Explian
Explain SELECT * FROM class LEFT JOIN book ON class.card= book.card
#可以看到第二行的type变为了ref,rows也成了优化比较明显
#这是由左连接特性决定的,LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有
#所以右边是关键点,一定需要建立索引
#删除旧索引 + 新建 +第三次explain
DROP INDEX Y ON book
ALTER TABLE class ADD INDEX X (card)
EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card
然后来看一个右连接查询
优化比较明显,这是因为RIGHT JOIN条件用于确定如何从左表搜索行,右边一定都有,
所以左边是关键点,一定需要建立索引
EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card
DROP INDEX X ON class;
ALTER TABLE book ADD INDEX Y(card)
#右连接,基本无变化
EXplian select * from class RIGHT JOIN book on class.card = book.card
案例三、三张表
三表
CREATE TABLE IF NOT EXISTS phone(
phoneid INT(10) UNSIGNED NULL AUTO_INCREMENT,
card INT(10) UNSIGNED NOT NULL,
PRIMARY KEY(phoneid)
)ENGINE=INNODB;
插入20条记录
INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20)));
删除原来旧索引
添加新的索引 ALTER TABLE phone ADD INDEX z(card)
ALTER TABLE book ADD INDEX Y(card) #上一个case建过一个同样的
Explain select * from class LEFT JOIN book ON class.card=book.card
LEFT JOIN phone ON book.card=phone.card
#后2行的type都是ref且rows优化很好,效果不错,因此索引最好设置在需要经常查询的字段中
结论
join语句的优化
1.尽可能减少join语句中的NestedLoop循环次数:“永远用小结果集驱动打的结果集”
2.优先优化NestedLoop的内层循环
3.保证Join语句中被驱动表上Join条件字段已经被索引;
当无法保证驱动表的Join条件字段被索引且内存资源充足的前提下,不要太利息JoinBuffer的设置