MySQL 优化之 EXPLAIN 关键字

来源:互联网 发布:java for循环有几种 编辑:程序博客网 时间:2024/05/01 03:43

MySQL查询优化之explain的深入解析

0. 准备

首先执行如下的 sql 语句:

CREATE TABLE IF NOT EXISTS `article` (`id` int(10) unsigned NOT NULL 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,PRIMARY KEY (`id`));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');

1. EXPLAIN 关键字

想要查询 category_id 为 1 且 comments 大于 1 的情况下,views 最多的 article_id。

语句如下(在传统的查询语句前加上 EXPLAIN 关键字):

EXPLAINSELECT author_idFROM `article`WHERE category_id = 1 AND comments > 1ORDER BY views DESCLIMIT 1;

给出如下的查询结果:

id: 1select_type: SIMPLEtable: articletype: ALLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 3Extra: Using where; Using filesort1 row in set (0.00 sec)

2. EXPLAIN 关键字与 MySQL 查询优化器

MySQL 查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。

EXPLAIN 的每个输出行提供一个表的相关信息,并且每个行包括下面的列:

  • id:MySQL Query Optimizer 选定的执行计划中查询的序列号。表示查询中执行 SELECT 子句或操作表的顺序,id 值越大优先级越高,越先被执行。id 相同,执行顺序由上至下。
  • select_type 查询类型
    • SIMPLE:简单的 select 查询,不使用 union 及子查询
    • PRIMARY:最外层的 select 查询
    • UNION:UNION 中的第二个或随后的 select 查询,不 依赖于外部查询的结果集
    • DEPENDENT UNION:UNION 中的第二个或随后的 select 查询,依 赖于外部查询的结果集
    • SUBQUERY:子查询中的第一个 select 查询,不依赖于外 部查询的结果集
    • DEPENDENT SUBQUERY:子查询中的第一个 select 查询,依赖于外部 查询的结果集
    • DERIVED:用于 from 子句里有子查询的情况。 MySQL 会 递归执行这些子查询, 把结果放在临时表里。
    • UNCACHEABLE SUBQUERY:结果集不能被缓存的子查询,必须重新为外 层查询的每一行进行评估。
    • UNCACHEABLE UNION:UNION 中的第二个或随后的 select 查询,属 于不可缓存的子查询
  • table:输出行所引用的表
  • extra:中出现以下 2 项意味着 MYSQL 根本不能使用索引,效率会受到重大影响。应尽可能对此进行优化。
    • Using filesort:表示 MySQL 会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容。可能在内存或者磁盘上进行排序。MySQL 中无法利用索引完成的排序操作称为“文件排序”
    • Using temporary:表示 MySQL 在对查询结果排序时使用临时表。常见于排序 order by 和分组查询 group by。

如果 EXPLAIN 的输出结果中,type 对应的是 ALL,extra 还出现了 Using filesort,都意味着 SQL 语句虽然能够工作,但其实是最坏的,并没有合理使用的 sql 查询语句。

3. 创建索引

ALTER TABLE `article` ADD INDEX x ( `category_id` , `comments`, `views` );

添加 INDEX 索引之后,再次调用 EXPLAIN 语句,可以看到,type 变成了 range,这是可以忍受的。但是 extra 里使用 Using filesort 仍是无法接受的。也即并未使用上面创建的索引。这是因为按照 BTree 索引的工作原理,先排序 category_id,如果遇到相同的 category_id 则再排序 comments,如果遇到相同的 comments 则再排序 views。当 comments 字段在联合索引里处于中间位置时,因comments > 1 条件是一个范围值,MySQL 无法利用索引再对后面的 views 部分进行检索,即 range 类型查询字段后面的索引无效。

DROP INDEX x ON article;ALTER TABLE `article` ADD INDEX y ( `category_id` , `views` ) ;