- 浏览: 564996 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (618)
- java (109)
- Java web (43)
- javascript (52)
- js (15)
- 闭包 (2)
- maven (8)
- 杂 (28)
- python (47)
- linux (51)
- git (18)
- (1)
- mysql (31)
- 管理 (1)
- redis (6)
- 操作系统 (12)
- 网络 (13)
- mongo (1)
- nginx (17)
- web (8)
- ffmpeg (1)
- python安装包 (0)
- php (49)
- imagemagic (1)
- eclipse (21)
- django (4)
- 学习 (1)
- 书籍 (1)
- uml (3)
- emacs (19)
- svn (2)
- netty (9)
- joomla (1)
- css (1)
- 推送 (2)
- android (6)
- memcached (2)
- docker、 (0)
- docker (7)
- go (1)
- resin (1)
- groovy (1)
- spring (1)
最新评论
-
chokee:
...
Spring3 MVC 深入研究 -
googleyufei:
很有用, 我现在打算学学Python. 这些资料的很及时.
python的几个实用网站(转的) -
hujingwei1001:
太好了找的就是它
easy explore -
xiangtui:
例子举得不错。。。学习了
java callback -
幻影桃花源:
太好了,謝謝
Spring3 MVC 深入研究
MySQL Explain 结果解读与实践
原文地址:http://blog.csdn.net/gevolution90/article/details/6856922
Explain 结果解读与实践
基于 MySQL 5.0.67 ,存储引擎 MyISAM 。
注:单独一行的"%%"及"`"表示分隔内容,就象分开“第一章”“第二章”。
explain 可以分析 select 语句的执行,即 MySQL 的“执行计划”:
mysql> explain select 1;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
用"\G"代替分号可得到竖排的格式:
mysql> explain select 1\G
*************************** 1
id: 1
select_type: SIMPLE
table: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: No tables used
`
可以用 desc 代替 explain :
desc select 1;
%%
id 列
建表:
create table a(a_id int);
create table b(b_id int);
create table c(c_id int);
mysql> explain select * from a join b on a_id=b_id where b_id in (select c_id from c);
+----+--------------------+-------+...
| id | select_type | table |...
+----+--------------------+-------+...
| 1 | PRIMARY | a |...
| 1 | PRIMARY | b |...
| 2 | DEPENDENT SUBQUERY | c |...
+----+--------------------+-------+...
从 3 个表中查询,对应输出 3 行,每行对应一个表, id 列表示执行顺序,id 越大,越先执行,id 相同,由上至下执行。此处的执行顺序为(以 table 列表示):c -> a -> b
%%
select_type 列
MySQL 把 SELECT 查询分成简单和复杂两种类型,复杂类型又可以分成三个大类:简单子查询、所谓的衍生表(子查询在 FROM 子句里)和 UNION 。
SIMPLE:查询中不包含子查询或者UNION
mysql> explain select * from a;
+----+-------------+-------+...
| id | select_type | table |...
+----+-------------+-------+...
| 1 | SIMPLE | a |...
+----+-------------+-------+...
SUBQUERY:子查询
PRIMARY:子查询上层
mysql> explain select * from a where a_id in (select b_id from b);
+----+--------------------+-------+...
| id | select_type | table |...
+----+--------------------+-------+...
| 1 | PRIMARY | a |...
| 2 | DEPENDENT SUBQUERY | b |...
+----+--------------------+-------+...
DERIVED:在FROM列表中包含子查询, MySQL 会递归执行这些子查询,把结果放在临时表里。
mysql> explain select count(*) from (select * from a) as der;
+----+-------------+-------+...
| id | select_type | table |...
+----+-------------+-------+...
| 1 | PRIMARY | NULL |...
| 2 | DERIVED | a |...
+----+-------------+-------+...
%%
table 列
显示每行对应的表名。若在 SELECT 语句中为表起了别名,则会显示表的别名。
一个很复杂的示例及解释可参考《高性能 MySQL 》(第二版)中文版 P467(pdf.491) 〈附录 B.2.3 table 列〉
%%
type 列
MySQL 在表里找到所需行的方式。包括(由左至右,由最差到最好):
| All | index | range | ref | eq_ref | const,system | null |
ALL
全表扫描,MySQL 从头到尾扫描整张表查找行。
mysql> explain select * from a\G
...
type: ALL
如果加上 limit 如 select * from a limit 10 MySQL 会扫描 10 行,但扫描方式不会变,还是从头到尾扫描。
`
index
按索引次序扫描表,就是先读索引,再读实际的行,其实还是全表扫描。主要优点是避免了排序,因为索引是排好序的。
建表:
create table a(a_id int not null, key(a_id));
insert into a value(1),(2)
mysql> explain select * from a\G
...
type: index
`
range
以范围的形式扫描索引
建表:
create table a(a_id int not null, key(a_id));
insert into a values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
mysql> explain select * from a where a_id > 1\G
...
type: range
...
IN 比较符也会用 range 表示:
mysql> explain select * from a where a_id in (1,3,4)\G
...
type: range
...
`
ref
非唯一性索引访问
建表:
create table a(a_id int not null, key(a_id));
insert into a values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
mysql> explain select * from a where a_id=1\G
...
type: ref
...
`
eq_ref
使用有唯一性索引查找(主键或唯一性索引)
建表及插入数据:
create table a(id int primary key);
create table a_info(id int primary key, title char(1));
insert into a value(1),(2);
insert into a_info value(1, 'a'),(2, 'b');
mysql> explain select * from a join a_info using(id);
...+--------+--------+...
...| table | type |...
...+--------+--------+...
...| a | index |...
...| a_info | eq_ref |...
...+--------+--------+...
此时 a_info 每条记录与 a 一一对应,通过主键 id 关联起来,所以 a_info 的 type 为 eq_ref。
删除 a_info 的主键:ALTER TABLE `a_info` DROP PRIMARY KEY;
现在 a_info 已经没有索引了:
mysql> explain select * from a join a_info using(id);
+----+...+--------+--------+...
| id |...| table | type |...
+----+...+--------+--------+...
| 1 |...| a_info | ALL |...
| 1 |...| a | eq_ref |...
+----+...+--------+--------+...
这次 MySQL 调整了执行顺序,先全表扫描 a_info 表,再对表 a 进行 eq_ref 查找,因为 a 表 id 还是主键。
删除 a 的主键:alter table a drop primary key;
现在 a 也没有索引了:
mysql> explain select * from a join a_info using(id);
...+--------+------+...
...| table | type |...
...+--------+------+...
...| a | ALL |...
...| a_info | ALL |...
...+--------+------+...
现在两个表都使用全表扫描了。
建表及插入数据:
create table a(id int primary key);
create table a_info(id int, title char(1), key(id));
insert into a value(1),(2);
insert into a_info value(1, 'a'),(2, 'b');
现在 a_info 表 id 列变为普通索引(非唯一性索引):
mysql> explain select * from a join a_info using(id) where a.id=1;
...+--------+-------+...
...| table | type |...
...+--------+-------+...
...| a | const |...
...| a_info | ref |...
...+--------+-------+...
a_info 表 type 变为 ref 类型了。
所以,唯一性索引才会出现 eq_ref (非唯一性索引会出现 ref ),因为唯一,所以最多只返回一条记录,找到后无需继续查找,因此比 ref 更快。
`
const
被称为“常量”,这个词不好理解,不过出现 const 的话就表示发生下面两种情况:
在整个查询过程中这个表最多只会有一条匹配的行,比如主键 id=1 就肯定只有一行,只需读取一次表数据便能取得所需的结果,且表数据在分解执行计划时读取。
返回值直接放在 select 语句中,类似 select 1 AS f 。可以通过 extended 选择查看内部过程:
建表及插入数据:
create table a(id int primary key, c1 char(20) not null, c2 text not null, c3 text not null);
insert into a values(1, 'asdfasdf', 'asdfasdf', 'asdfasdf'), (2, 'asdfasdf', 'asdfasdf', 'asdfasdf');
mysql> explain extended select * from a where id=1\G
...
type: const
possible_keys: PRIMARY
key: PRIMARY
...
用 show warnings 查看 MySQL 是如何优化的:
mysql> show warnings\G
...
Message: select '1' AS `id`,'asdfasdf' AS `c1`,'asdfasdf' AS `c2`,'asdfasdf' AS
`c3` from `test`.`a` where 1
查询返回的结果为:
mysql> select * from a where id=1;
+----+----------+----------+----------+
| id | c1 | c2 | c3 |
+----+----------+----------+----------+
| 1 | asdfasdf | asdfasdf | asdfasdf |
+----+----------+----------+----------+
可以看出,返回结果中的字段值都以“值 AS 字段名”的形式直接出现在优化后的 select 语句中。
修改一下查询:
mysql> explain select * from a where id in(1,2)\G
...
type: range
...
当返回结果超过 1 条时, type 便不再为 const 了。
重新建表及插入数据:
create table a (id int not null);
insert into a value(1),(2),(3);
mysql> explain select * from a where id=1\G
...
type: ALL
目前表中只有一条 id=1 的记录,但 type 已为 ALL ,因为只有唯一性索引才能保证表中最多只有一条记录,只有这样 type 才有可能为 const 。
为 id 加普通索引后, type 变为 ref ,改为加唯一或主键索引后, type 便变为 const 了。
相关阅读:
《高性能 MySQL 》第 2 版中文版
P130(pdf.153) 计算和减少常量表达式
P471(pdf.495) B.2.4 type 列
《 MySQL 性能调优与架构设计》
pdf.109
MySQL 帮助手册 7.2.1. EXPLAIN语法(获取SELECT相关信息)
`
system
system 是 const 类型的特例,当表只有一行时就会出现 system 。
建表及插入数据:
create table a(id int primary key);
insert into a value(1);
mysql> explain select * from a\G
...
type: system
...
`
NULL
在优化过程中就已得到结果,不用再访问表或索引。
mysql> explain select min(a_id) from a\G
...
type: NULL
...
%%
possible_keys 列
可能被用到的索引
建表:
create table a (a_id int primary key, a_age int, key (a_id, a_age));
此表有 主键及普通索引 两个索引。
mysql> explain select * from a where a_id=1\G
...
possible_keys: PRIMARY,a_id
%%
key 列
查询过程中实际使用的索引
mysql> explain select * from a where a_id=1\G
...
possible_keys: PRIMARY,a_id
key: PRIMARY
%%
key_len 列
索引字段最大可能使用的长度。
mysql> explain select * from a where a_id=1\G
...
key: PRIMARY
key_len: 4
a_id 是 int 类型,int 的长度是 4 字节,所以 key_len 为 4。
%%
ref 列
指出对 key 列所选择的索引的查找方式,常见的值有 const, func, NULL, 具体字段名。当 key 列为 NULL ,即不使用索引时,此值也相应的为 NULL 。
建表及插入数据:
create table a(id int primary key, age int);
insert into a value(1, 10),(2, 10);
mysql> desc select * from a where age=10\G
...
key: NULL
key_len: NULL
ref: NULL
...
当 key 列为 NULL , ref 列也相应为 NULL 。
mysql> explain select * from a where id=1\G
...
key: PRIMARY
key_len: 4
ref: const
这次 key 列使用了主键索引,where id=1 中 1 为常量, ref 列的 const 便是指这种常量。
mysql> explain select * from a where id in (1,2)\G
...
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
...
不理解 ref 为 NULL 的含意,比如上面这个查询, key 列有使用索引,但 ref 列却为 NULL 。网上搜索及查阅了一下 MySQL 帮助手册都没有找到相关的描述。
再建表及插入数据:
create table a(id int primary key, a_name int not null);
create table b(id int primary key, b_name int not null);
insert into a value(1, 1),(2, 2),(3, 3);
insert into b value(1, 111),(2, 222),(3, 333);
mysql> explain select * from a join b using(id);
...+-------+--------+...+---------+...+-----------+...
...| table | type |...| key |...| ref |...
...+-------+--------+...+---------+...+-----------+...
...| a | ALL |...| NULL |...| NULL |...
...| b | eq_ref |...| PRIMARY |...| test.a.id |...
...+-------+--------+...+---------+...+-----------+...
这里 test.a.id 即为具体字段,意为根据表 a 的 id 字段的值查找表 b 的主键索引。
mysql> explain select * from a join b using(id) where b.id=1;
...+-------+-------+...+---------+...+-------+...
...| table | type |...| key |...| ref |...
...+-------+-------+...+---------+...+-------+...
...| a | const |...| PRIMARY |...| const |...
...| b | const |...| PRIMARY |...| const |...
...+-------+-------+...+---------+...+-------+...
因为 a join b 的条件为 id 相等,而 b.id=1 ,就是 a.id 也为 1 ,所以 a,b 两个表的 ref 列都为 const 。
ref 为 func 的情况出现在子查询中,暂不明其原理:
mysql> explain select * from a where id in (select id from b where id in (1,2));
+----+--------------------+-------+...+---------+...+------+...
| id | select_type | table |...| key |...| ref |...
+----+--------------------+-------+...+---------+...+------+...
| 1 | PRIMARY | a |...| NULL |...| NULL |...
| 2 | DEPENDENT SUBQUERY | b |...| PRIMARY |...| func |...
+----+--------------------+-------+...+---------+...+------+...
%%
rows 列
MySQL 估计的需要扫描的行数。只是一个估计。
%%
Extra 列
显示上述信息之外的其它信息,但却很重要。
Using index
此查询使用了覆盖索引(Covering Index),即通过索引就能返回结果,无需访问表。
若没显示"Using index"表示读取了表数据。
建表及插入数据:
create table a (id int primary key, age int);
insert into a value(1, 10),(2, 10);
mysql> explain select id from a\G
...
Extra: Using index
因为 id 为主键索引,索引中直接包含了 id 的值,所以无需访问表,直接查找索引就能返回结果。
mysql> explain select age from a\G
...
Extra:
age 列没有索引,因此没有 Using index ,意即需要访问表。
为 age 列添加索引:create index age on a(id, age);
mysql> explain select age from a\G
...
Extra: Using index
现在索引 age 中也包含了 age 列的值,因此不用访问表便能返回结果了。
建表:create table a(id int auto_increment primary key, age int, name char(10));
插入 100w 条数据:insert into a value(null, rand()*100000000, 'jack');
`
Using where
表示 MySQL 服务器从存储引擎收到行后再进行“后过滤”(Post-filter)。所谓“后过滤”,就是先读取整行数据,再检查此行是否符合 where 句的条件,符合就留下,不符合便丢弃。因为检查是在读取行后才进行的,所以称为“后过滤”。
建表及插入数据:
create table a (num_a int not null, num_b int not null, key(num_a));
insert into a value(1,1),(1,2),(2,1),(2,2);
mysql> explain select * from a where num_a=1\G
...
type: ref
possible_keys: num_a
key: num_a
key_len: 4
...
Extra:
虽然查询中有 where 子句,但只有 num_a=1 一个条件,且 num_a 列存在索引,通过索引便能确定返回的行,无需进行“后过滤”。
所以,并非带 WHERE 子句就会显示"Using where"的。
mysql> explain select * from a where num_a=1 and num_b=1\G
...
type: ref
possible_keys: num_a
key: num_a
...
Extra: Using where
此查询增加了条件 num_b=1 ,此列没有索引,但可以看到查询同样能使用 num_a 索引。 MySQL 先通过索引 num_a 找到 num_a=1 的行,然后读取整行数据,再检查 num_b 是否等于 1 ,执行过程看上去象这样:
num_a索引|num_b 没有索引,属于行数据
+-------+-------+
| num_a | num_b | where 子句(num_b=1)
+-------+-------+
| 1 | 1 | 符合
| 1 | 2 | 不符合
| ... | ... | ...
+-------+-------+
在《高性能 MySQL 》(第二版)P144(pdf.167) 页有更形象的说明图片(图 4-5 MySQL 通过整表扫描查找数据)。
字段是否允许 NULL 对 Using where 的影响:
建表及插入数据:
create table a (num_a int null, num_b int null, key(num_a));
insert into a value(1,1),(1,2),(2,1),(2,2);
这次 num_a, num_b 字段允许为空。
在上例 num_a not null 时, num_a 索引的长度 key_len 为 4 ,当 num_a null 时, num_a 索引的长度变为了 5 :
mysql> explain select * from a where num_a=1\G
...
type: ref
possible_keys: num_a
key: num_a
key_len: 5
...
Extra: Using where
并且哪怕只有 num_a=1 一个条件,也会出现 Using where 。原因暂不明白。
`
Using temporary
使用到临时表
建表及插入数据:
create table a(a_id int, b_id int);
insert into a values(1,1),(1,1),(2,1),(2,2),(3,1);
mysql> explain select distinct a_id from a\G
...
Extra: Using temporary
MySQL 使用临时表来实现 distinct 操作。
`
Using filesort
若查询所需的排序与使用的索引的排序一致,因为索引是已排序的,因此按索引的顺序读取结果返回,否则,在取得结果后,还需要按查询所需的顺序对结果进行排序,这时就会出现 Using filesort 。
建表及插入数据:
create table a(a_id int, b_id int);
insert into a values(1,1),(1,1),(2,1),(2,2),(3,1);
mysql> explain select * from a order by a_id\G
...
Extra: Using filesort
对于没有索引的表,只要 order by 必会出现 Using filesort 。
现在增加索引:create index a_id on a(a_id);
把表 a 的记录增加到约 100w(1048576) 条, a_id 与 b_id 都是随机生成的数字:
mysql> select * from a order by rand() limit 10;
+-------+--------+
| a_id | b_id |
+-------+--------+
| 61566 | 961297 |
| 33951 | 680542 |
| ..... | ...... |
+-------+--------+
mysql> explain select * from a order by a_id\G
...
type: ALL
...
rows: 1048576
Extra: Using filesort
同样是 Using filesort ,type 为 ALL ,全表扫描。听说“取全表数据根据ID排序,走索引一定不如直接查,因为可以减少因为需要索引改变数据访问顺序造成随机IO的概率,数据库放弃索引是应该的”,参考:
http://isky000.com/database/mysql_order_by_implement#comment-2981
当 type 为 rang、 ref 或者 index 的时候才有可能利用索引排序,其它,如 ALL ,都无法通过索引排序,此时若有 order by ,如上例,便会出现 Using filesort 。
现在增加 where 子句:
mysql> explain select * from a where a_id=10 order by a_id\G
...
type: ref
possible_keys: a_id
key: a_id
...
rows: 8
Extra:
查询走了索引 a_id ,此时 type 为 ref ,直接按索引顺序返回,没有 Using filesort 。
修改 where 子句:
mysql> explain select * from a where a_id>10 and a_id<100 order by a_id\G
...
type: range
possible_keys: a_id
key: a_id
...
rows: 712
Extra: Using where
同样利用索引排序,没有 Using filesort 。
再修改 where 子句:
mysql> explain select * from a where a_id >10 order by a_id\G
...
type: ALL
possible_keys: a_id
key: NULL
...
rows: 1048576
Extra: Using where; Using filesort
又出现 Using filesort 且 type 变为 ALL 。注意以上例子的 rows 列,此列表示 MySQL 估计查询需要读取的行数,分别为 1048576, 8, 712, 1048576 ,特别注意最后两个数字: 712, 1048576 。
可见,当索引能为查询排除大部份行时( a_id=10 时约读取 8 行,排除了大部份, a_id>10 and a_id<100 时约读取 712 行,同样排除了大部份)便使用索引,否则,如 a_id>10 时约读取 1048576 , MySQL 直接改用全表扫描,再 Using filesort 。也就是说, MySQL 会根据表中的信息及查询来决定使用任种方式。
关于 MySQL 读取数据表的方式,可参考(暂缺参考资料),就会明白为什么需读取 1048576 行时,先读索引再读表数据还不如全表扫描了。
对于多字段排序(order by a, b)及带 group by 的查询,可参考 MySQL 帮助手册 7.2.12. MySQL如何优化ORDER BY 。
%%
参考资料:
MySQL 帮助手册
7.2.1. EXPLAIN语法(获取SELECT相关信息)
7.2.12. MySQL如何优化ORDER BY
《高性能 MySQL 》(第二版)中文版
P463(pdf.487) 附录 B
《 MySQL 性能调优与架构设计》
MySQL ORDER BY 的实现分析
http://isky000.com/database/mysql_order_by_implement
Mysql执行计划
http://www.xifenfei.com/954.html
MySQL执行计划解读
http://wenku.baidu.com/view/41846439376baf1ffc4fad4f.html
Mysql Explain 详解
http://www.cnitblog.com/aliyiyi08/archive/2008/09/09/48878.html
MYSQL EXPLAIN语句的extended 选项学习体会
http://hi.baidu.com/dearhwj/blog/item/03badf17641a28094a90a78d.html
Show Warnings
%%
MySQL索引类型一览 让MySQL高效运行起来
http://database.51cto.com/art/200910/156685.htm
Mysql执行计划中的Using filesort
"指出Mysql可以使用哪个索引在该表中找到行"
http://www.taobaodba.com/html/235_mysql_using_filesort.html
2.order by b,如果b列不在索引中,不管b值是否相同,总会出现Using filesort。
%%
What does Using filesort mean in MySQL?
"任何不能通过index进行的sort都称之为filesort"
http://iceskysl.1sters.com/?p=638
mysql explain中的using filesort
"只能是如果可能的话修改查询的排序条件"
http://blog.csdn.net/wdwbw/article/details/5256064
Mysql之EXPLAIN显示using filesort
"结果昨天看到公司的一个"
http://www.ccvita.com/169.html(可能这个是出处)
多列索引在建立的时候是以B-树结构建立的,因此建立索引的时候是先建立ID的按顺序排的索引,在相同ID的情况下建立FID按 顺序排的索引,最后在FID 相同的情况下建立按INVERSE_DATE顺序排的索引,如果列数更多以此类推。
要在优化一下这个sql就应该为它建立另一个索引IDX(ID,INVERSE_DATE),这样就消除了using filesort速度也会快很多。
%%
mysql优化Using filesort
"并且用到了room_number这列索引"
http://www.askwan.com/post/151/
MySQL 索引 优化 Using filesort
"页面咔的一下就出来了"
http://www.leakon.com/archives/332
Explain 结果解读与实践
基于 MySQL 5.0.67 ,存储引擎 MyISAM 。
注:单独一行的"%%"及"`"表示分隔内容,就象分开“第一章”“第二章”。
explain 可以分析 select 语句的执行,即 MySQL 的“执行计划”:
mysql> explain select 1;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
用"\G"代替分号可得到竖排的格式:
mysql> explain select 1\G
*************************** 1
id: 1
select_type: SIMPLE
table: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: No tables used
`
可以用 desc 代替 explain :
desc select 1;
%%
id 列
建表:
create table a(a_id int);
create table b(b_id int);
create table c(c_id int);
mysql> explain select * from a join b on a_id=b_id where b_id in (select c_id from c);
+----+--------------------+-------+...
| id | select_type | table |...
+----+--------------------+-------+...
| 1 | PRIMARY | a |...
| 1 | PRIMARY | b |...
| 2 | DEPENDENT SUBQUERY | c |...
+----+--------------------+-------+...
从 3 个表中查询,对应输出 3 行,每行对应一个表, id 列表示执行顺序,id 越大,越先执行,id 相同,由上至下执行。此处的执行顺序为(以 table 列表示):c -> a -> b
%%
select_type 列
MySQL 把 SELECT 查询分成简单和复杂两种类型,复杂类型又可以分成三个大类:简单子查询、所谓的衍生表(子查询在 FROM 子句里)和 UNION 。
SIMPLE:查询中不包含子查询或者UNION
mysql> explain select * from a;
+----+-------------+-------+...
| id | select_type | table |...
+----+-------------+-------+...
| 1 | SIMPLE | a |...
+----+-------------+-------+...
SUBQUERY:子查询
PRIMARY:子查询上层
mysql> explain select * from a where a_id in (select b_id from b);
+----+--------------------+-------+...
| id | select_type | table |...
+----+--------------------+-------+...
| 1 | PRIMARY | a |...
| 2 | DEPENDENT SUBQUERY | b |...
+----+--------------------+-------+...
DERIVED:在FROM列表中包含子查询, MySQL 会递归执行这些子查询,把结果放在临时表里。
mysql> explain select count(*) from (select * from a) as der;
+----+-------------+-------+...
| id | select_type | table |...
+----+-------------+-------+...
| 1 | PRIMARY | NULL |...
| 2 | DERIVED | a |...
+----+-------------+-------+...
%%
table 列
显示每行对应的表名。若在 SELECT 语句中为表起了别名,则会显示表的别名。
一个很复杂的示例及解释可参考《高性能 MySQL 》(第二版)中文版 P467(pdf.491) 〈附录 B.2.3 table 列〉
%%
type 列
MySQL 在表里找到所需行的方式。包括(由左至右,由最差到最好):
| All | index | range | ref | eq_ref | const,system | null |
ALL
全表扫描,MySQL 从头到尾扫描整张表查找行。
mysql> explain select * from a\G
...
type: ALL
如果加上 limit 如 select * from a limit 10 MySQL 会扫描 10 行,但扫描方式不会变,还是从头到尾扫描。
`
index
按索引次序扫描表,就是先读索引,再读实际的行,其实还是全表扫描。主要优点是避免了排序,因为索引是排好序的。
建表:
create table a(a_id int not null, key(a_id));
insert into a value(1),(2)
mysql> explain select * from a\G
...
type: index
`
range
以范围的形式扫描索引
建表:
create table a(a_id int not null, key(a_id));
insert into a values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
mysql> explain select * from a where a_id > 1\G
...
type: range
...
IN 比较符也会用 range 表示:
mysql> explain select * from a where a_id in (1,3,4)\G
...
type: range
...
`
ref
非唯一性索引访问
建表:
create table a(a_id int not null, key(a_id));
insert into a values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
mysql> explain select * from a where a_id=1\G
...
type: ref
...
`
eq_ref
使用有唯一性索引查找(主键或唯一性索引)
建表及插入数据:
create table a(id int primary key);
create table a_info(id int primary key, title char(1));
insert into a value(1),(2);
insert into a_info value(1, 'a'),(2, 'b');
mysql> explain select * from a join a_info using(id);
...+--------+--------+...
...| table | type |...
...+--------+--------+...
...| a | index |...
...| a_info | eq_ref |...
...+--------+--------+...
此时 a_info 每条记录与 a 一一对应,通过主键 id 关联起来,所以 a_info 的 type 为 eq_ref。
删除 a_info 的主键:ALTER TABLE `a_info` DROP PRIMARY KEY;
现在 a_info 已经没有索引了:
mysql> explain select * from a join a_info using(id);
+----+...+--------+--------+...
| id |...| table | type |...
+----+...+--------+--------+...
| 1 |...| a_info | ALL |...
| 1 |...| a | eq_ref |...
+----+...+--------+--------+...
这次 MySQL 调整了执行顺序,先全表扫描 a_info 表,再对表 a 进行 eq_ref 查找,因为 a 表 id 还是主键。
删除 a 的主键:alter table a drop primary key;
现在 a 也没有索引了:
mysql> explain select * from a join a_info using(id);
...+--------+------+...
...| table | type |...
...+--------+------+...
...| a | ALL |...
...| a_info | ALL |...
...+--------+------+...
现在两个表都使用全表扫描了。
建表及插入数据:
create table a(id int primary key);
create table a_info(id int, title char(1), key(id));
insert into a value(1),(2);
insert into a_info value(1, 'a'),(2, 'b');
现在 a_info 表 id 列变为普通索引(非唯一性索引):
mysql> explain select * from a join a_info using(id) where a.id=1;
...+--------+-------+...
...| table | type |...
...+--------+-------+...
...| a | const |...
...| a_info | ref |...
...+--------+-------+...
a_info 表 type 变为 ref 类型了。
所以,唯一性索引才会出现 eq_ref (非唯一性索引会出现 ref ),因为唯一,所以最多只返回一条记录,找到后无需继续查找,因此比 ref 更快。
`
const
被称为“常量”,这个词不好理解,不过出现 const 的话就表示发生下面两种情况:
在整个查询过程中这个表最多只会有一条匹配的行,比如主键 id=1 就肯定只有一行,只需读取一次表数据便能取得所需的结果,且表数据在分解执行计划时读取。
返回值直接放在 select 语句中,类似 select 1 AS f 。可以通过 extended 选择查看内部过程:
建表及插入数据:
create table a(id int primary key, c1 char(20) not null, c2 text not null, c3 text not null);
insert into a values(1, 'asdfasdf', 'asdfasdf', 'asdfasdf'), (2, 'asdfasdf', 'asdfasdf', 'asdfasdf');
mysql> explain extended select * from a where id=1\G
...
type: const
possible_keys: PRIMARY
key: PRIMARY
...
用 show warnings 查看 MySQL 是如何优化的:
mysql> show warnings\G
...
Message: select '1' AS `id`,'asdfasdf' AS `c1`,'asdfasdf' AS `c2`,'asdfasdf' AS
`c3` from `test`.`a` where 1
查询返回的结果为:
mysql> select * from a where id=1;
+----+----------+----------+----------+
| id | c1 | c2 | c3 |
+----+----------+----------+----------+
| 1 | asdfasdf | asdfasdf | asdfasdf |
+----+----------+----------+----------+
可以看出,返回结果中的字段值都以“值 AS 字段名”的形式直接出现在优化后的 select 语句中。
修改一下查询:
mysql> explain select * from a where id in(1,2)\G
...
type: range
...
当返回结果超过 1 条时, type 便不再为 const 了。
重新建表及插入数据:
create table a (id int not null);
insert into a value(1),(2),(3);
mysql> explain select * from a where id=1\G
...
type: ALL
目前表中只有一条 id=1 的记录,但 type 已为 ALL ,因为只有唯一性索引才能保证表中最多只有一条记录,只有这样 type 才有可能为 const 。
为 id 加普通索引后, type 变为 ref ,改为加唯一或主键索引后, type 便变为 const 了。
相关阅读:
《高性能 MySQL 》第 2 版中文版
P130(pdf.153) 计算和减少常量表达式
P471(pdf.495) B.2.4 type 列
《 MySQL 性能调优与架构设计》
pdf.109
MySQL 帮助手册 7.2.1. EXPLAIN语法(获取SELECT相关信息)
`
system
system 是 const 类型的特例,当表只有一行时就会出现 system 。
建表及插入数据:
create table a(id int primary key);
insert into a value(1);
mysql> explain select * from a\G
...
type: system
...
`
NULL
在优化过程中就已得到结果,不用再访问表或索引。
mysql> explain select min(a_id) from a\G
...
type: NULL
...
%%
possible_keys 列
可能被用到的索引
建表:
create table a (a_id int primary key, a_age int, key (a_id, a_age));
此表有 主键及普通索引 两个索引。
mysql> explain select * from a where a_id=1\G
...
possible_keys: PRIMARY,a_id
%%
key 列
查询过程中实际使用的索引
mysql> explain select * from a where a_id=1\G
...
possible_keys: PRIMARY,a_id
key: PRIMARY
%%
key_len 列
索引字段最大可能使用的长度。
mysql> explain select * from a where a_id=1\G
...
key: PRIMARY
key_len: 4
a_id 是 int 类型,int 的长度是 4 字节,所以 key_len 为 4。
%%
ref 列
指出对 key 列所选择的索引的查找方式,常见的值有 const, func, NULL, 具体字段名。当 key 列为 NULL ,即不使用索引时,此值也相应的为 NULL 。
建表及插入数据:
create table a(id int primary key, age int);
insert into a value(1, 10),(2, 10);
mysql> desc select * from a where age=10\G
...
key: NULL
key_len: NULL
ref: NULL
...
当 key 列为 NULL , ref 列也相应为 NULL 。
mysql> explain select * from a where id=1\G
...
key: PRIMARY
key_len: 4
ref: const
这次 key 列使用了主键索引,where id=1 中 1 为常量, ref 列的 const 便是指这种常量。
mysql> explain select * from a where id in (1,2)\G
...
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
...
不理解 ref 为 NULL 的含意,比如上面这个查询, key 列有使用索引,但 ref 列却为 NULL 。网上搜索及查阅了一下 MySQL 帮助手册都没有找到相关的描述。
再建表及插入数据:
create table a(id int primary key, a_name int not null);
create table b(id int primary key, b_name int not null);
insert into a value(1, 1),(2, 2),(3, 3);
insert into b value(1, 111),(2, 222),(3, 333);
mysql> explain select * from a join b using(id);
...+-------+--------+...+---------+...+-----------+...
...| table | type |...| key |...| ref |...
...+-------+--------+...+---------+...+-----------+...
...| a | ALL |...| NULL |...| NULL |...
...| b | eq_ref |...| PRIMARY |...| test.a.id |...
...+-------+--------+...+---------+...+-----------+...
这里 test.a.id 即为具体字段,意为根据表 a 的 id 字段的值查找表 b 的主键索引。
mysql> explain select * from a join b using(id) where b.id=1;
...+-------+-------+...+---------+...+-------+...
...| table | type |...| key |...| ref |...
...+-------+-------+...+---------+...+-------+...
...| a | const |...| PRIMARY |...| const |...
...| b | const |...| PRIMARY |...| const |...
...+-------+-------+...+---------+...+-------+...
因为 a join b 的条件为 id 相等,而 b.id=1 ,就是 a.id 也为 1 ,所以 a,b 两个表的 ref 列都为 const 。
ref 为 func 的情况出现在子查询中,暂不明其原理:
mysql> explain select * from a where id in (select id from b where id in (1,2));
+----+--------------------+-------+...+---------+...+------+...
| id | select_type | table |...| key |...| ref |...
+----+--------------------+-------+...+---------+...+------+...
| 1 | PRIMARY | a |...| NULL |...| NULL |...
| 2 | DEPENDENT SUBQUERY | b |...| PRIMARY |...| func |...
+----+--------------------+-------+...+---------+...+------+...
%%
rows 列
MySQL 估计的需要扫描的行数。只是一个估计。
%%
Extra 列
显示上述信息之外的其它信息,但却很重要。
Using index
此查询使用了覆盖索引(Covering Index),即通过索引就能返回结果,无需访问表。
若没显示"Using index"表示读取了表数据。
建表及插入数据:
create table a (id int primary key, age int);
insert into a value(1, 10),(2, 10);
mysql> explain select id from a\G
...
Extra: Using index
因为 id 为主键索引,索引中直接包含了 id 的值,所以无需访问表,直接查找索引就能返回结果。
mysql> explain select age from a\G
...
Extra:
age 列没有索引,因此没有 Using index ,意即需要访问表。
为 age 列添加索引:create index age on a(id, age);
mysql> explain select age from a\G
...
Extra: Using index
现在索引 age 中也包含了 age 列的值,因此不用访问表便能返回结果了。
建表:create table a(id int auto_increment primary key, age int, name char(10));
插入 100w 条数据:insert into a value(null, rand()*100000000, 'jack');
`
Using where
表示 MySQL 服务器从存储引擎收到行后再进行“后过滤”(Post-filter)。所谓“后过滤”,就是先读取整行数据,再检查此行是否符合 where 句的条件,符合就留下,不符合便丢弃。因为检查是在读取行后才进行的,所以称为“后过滤”。
建表及插入数据:
create table a (num_a int not null, num_b int not null, key(num_a));
insert into a value(1,1),(1,2),(2,1),(2,2);
mysql> explain select * from a where num_a=1\G
...
type: ref
possible_keys: num_a
key: num_a
key_len: 4
...
Extra:
虽然查询中有 where 子句,但只有 num_a=1 一个条件,且 num_a 列存在索引,通过索引便能确定返回的行,无需进行“后过滤”。
所以,并非带 WHERE 子句就会显示"Using where"的。
mysql> explain select * from a where num_a=1 and num_b=1\G
...
type: ref
possible_keys: num_a
key: num_a
...
Extra: Using where
此查询增加了条件 num_b=1 ,此列没有索引,但可以看到查询同样能使用 num_a 索引。 MySQL 先通过索引 num_a 找到 num_a=1 的行,然后读取整行数据,再检查 num_b 是否等于 1 ,执行过程看上去象这样:
num_a索引|num_b 没有索引,属于行数据
+-------+-------+
| num_a | num_b | where 子句(num_b=1)
+-------+-------+
| 1 | 1 | 符合
| 1 | 2 | 不符合
| ... | ... | ...
+-------+-------+
在《高性能 MySQL 》(第二版)P144(pdf.167) 页有更形象的说明图片(图 4-5 MySQL 通过整表扫描查找数据)。
字段是否允许 NULL 对 Using where 的影响:
建表及插入数据:
create table a (num_a int null, num_b int null, key(num_a));
insert into a value(1,1),(1,2),(2,1),(2,2);
这次 num_a, num_b 字段允许为空。
在上例 num_a not null 时, num_a 索引的长度 key_len 为 4 ,当 num_a null 时, num_a 索引的长度变为了 5 :
mysql> explain select * from a where num_a=1\G
...
type: ref
possible_keys: num_a
key: num_a
key_len: 5
...
Extra: Using where
并且哪怕只有 num_a=1 一个条件,也会出现 Using where 。原因暂不明白。
`
Using temporary
使用到临时表
建表及插入数据:
create table a(a_id int, b_id int);
insert into a values(1,1),(1,1),(2,1),(2,2),(3,1);
mysql> explain select distinct a_id from a\G
...
Extra: Using temporary
MySQL 使用临时表来实现 distinct 操作。
`
Using filesort
若查询所需的排序与使用的索引的排序一致,因为索引是已排序的,因此按索引的顺序读取结果返回,否则,在取得结果后,还需要按查询所需的顺序对结果进行排序,这时就会出现 Using filesort 。
建表及插入数据:
create table a(a_id int, b_id int);
insert into a values(1,1),(1,1),(2,1),(2,2),(3,1);
mysql> explain select * from a order by a_id\G
...
Extra: Using filesort
对于没有索引的表,只要 order by 必会出现 Using filesort 。
现在增加索引:create index a_id on a(a_id);
把表 a 的记录增加到约 100w(1048576) 条, a_id 与 b_id 都是随机生成的数字:
mysql> select * from a order by rand() limit 10;
+-------+--------+
| a_id | b_id |
+-------+--------+
| 61566 | 961297 |
| 33951 | 680542 |
| ..... | ...... |
+-------+--------+
mysql> explain select * from a order by a_id\G
...
type: ALL
...
rows: 1048576
Extra: Using filesort
同样是 Using filesort ,type 为 ALL ,全表扫描。听说“取全表数据根据ID排序,走索引一定不如直接查,因为可以减少因为需要索引改变数据访问顺序造成随机IO的概率,数据库放弃索引是应该的”,参考:
http://isky000.com/database/mysql_order_by_implement#comment-2981
当 type 为 rang、 ref 或者 index 的时候才有可能利用索引排序,其它,如 ALL ,都无法通过索引排序,此时若有 order by ,如上例,便会出现 Using filesort 。
现在增加 where 子句:
mysql> explain select * from a where a_id=10 order by a_id\G
...
type: ref
possible_keys: a_id
key: a_id
...
rows: 8
Extra:
查询走了索引 a_id ,此时 type 为 ref ,直接按索引顺序返回,没有 Using filesort 。
修改 where 子句:
mysql> explain select * from a where a_id>10 and a_id<100 order by a_id\G
...
type: range
possible_keys: a_id
key: a_id
...
rows: 712
Extra: Using where
同样利用索引排序,没有 Using filesort 。
再修改 where 子句:
mysql> explain select * from a where a_id >10 order by a_id\G
...
type: ALL
possible_keys: a_id
key: NULL
...
rows: 1048576
Extra: Using where; Using filesort
又出现 Using filesort 且 type 变为 ALL 。注意以上例子的 rows 列,此列表示 MySQL 估计查询需要读取的行数,分别为 1048576, 8, 712, 1048576 ,特别注意最后两个数字: 712, 1048576 。
可见,当索引能为查询排除大部份行时( a_id=10 时约读取 8 行,排除了大部份, a_id>10 and a_id<100 时约读取 712 行,同样排除了大部份)便使用索引,否则,如 a_id>10 时约读取 1048576 , MySQL 直接改用全表扫描,再 Using filesort 。也就是说, MySQL 会根据表中的信息及查询来决定使用任种方式。
关于 MySQL 读取数据表的方式,可参考(暂缺参考资料),就会明白为什么需读取 1048576 行时,先读索引再读表数据还不如全表扫描了。
对于多字段排序(order by a, b)及带 group by 的查询,可参考 MySQL 帮助手册 7.2.12. MySQL如何优化ORDER BY 。
%%
参考资料:
MySQL 帮助手册
7.2.1. EXPLAIN语法(获取SELECT相关信息)
7.2.12. MySQL如何优化ORDER BY
《高性能 MySQL 》(第二版)中文版
P463(pdf.487) 附录 B
《 MySQL 性能调优与架构设计》
MySQL ORDER BY 的实现分析
http://isky000.com/database/mysql_order_by_implement
Mysql执行计划
http://www.xifenfei.com/954.html
MySQL执行计划解读
http://wenku.baidu.com/view/41846439376baf1ffc4fad4f.html
Mysql Explain 详解
http://www.cnitblog.com/aliyiyi08/archive/2008/09/09/48878.html
MYSQL EXPLAIN语句的extended 选项学习体会
http://hi.baidu.com/dearhwj/blog/item/03badf17641a28094a90a78d.html
Show Warnings
%%
MySQL索引类型一览 让MySQL高效运行起来
http://database.51cto.com/art/200910/156685.htm
Mysql执行计划中的Using filesort
"指出Mysql可以使用哪个索引在该表中找到行"
http://www.taobaodba.com/html/235_mysql_using_filesort.html
2.order by b,如果b列不在索引中,不管b值是否相同,总会出现Using filesort。
%%
What does Using filesort mean in MySQL?
"任何不能通过index进行的sort都称之为filesort"
http://iceskysl.1sters.com/?p=638
mysql explain中的using filesort
"只能是如果可能的话修改查询的排序条件"
http://blog.csdn.net/wdwbw/article/details/5256064
Mysql之EXPLAIN显示using filesort
"结果昨天看到公司的一个"
http://www.ccvita.com/169.html(可能这个是出处)
多列索引在建立的时候是以B-树结构建立的,因此建立索引的时候是先建立ID的按顺序排的索引,在相同ID的情况下建立FID按 顺序排的索引,最后在FID 相同的情况下建立按INVERSE_DATE顺序排的索引,如果列数更多以此类推。
要在优化一下这个sql就应该为它建立另一个索引IDX(ID,INVERSE_DATE),这样就消除了using filesort速度也会快很多。
%%
mysql优化Using filesort
"并且用到了room_number这列索引"
http://www.askwan.com/post/151/
MySQL 索引 优化 Using filesort
"页面咔的一下就出来了"
http://www.leakon.com/archives/332
相关推荐
本书“MySQL性能调优与架构设计高清全册”深入探讨了如何优化MySQL的性能并设计高效的数据架构,以满足大规模应用的需求。以下是对该书内容的详细解读: 1. **SQL基础与优化**:书中首先会讲解SQL语言的基本概念,...
通过阅读这份官方文档,无论是初学者还是经验丰富的DBA,都能深入了解MySQL 5.6的功能和最佳实践,提升数据库管理能力。"refman-5.6-en.html-chapter"这个文件名暗示文档是英文版的手册章节,读者可以按章节逐个深入...
在性能调优方面,读者将学习如何分析查询性能,利用EXPLAIN命令解读查询计划,优化SQL语句,以及调整索引策略。此外,还会涉及内存管理、连接池配置、查询缓存等高级话题,以实现更高效的资源利用。数据库设计也是...
- **MySQL新特性**:可能包括对MySQL最新版本的新特性的解读,如InnoDB Cluster、JSON支持、窗口函数等。 总之,"MYSQL高效编程.pdf"这本书应该是一本全面介绍MySQL高效使用和管理的指南,涵盖了从基础到高级的...
书中介绍了如何使用MySQL内置的性能工具,如SHOW STATUS、SHOW VARIABLES、EXPLAIN等,以及如何解读这些信息来发现和解决性能瓶颈。此外,还涉及到了如何调整MySQL的配置参数以优化性能。 六、复制与高可用性 MySQL...
《MySQL5.1.0中文使用手册》是开发者、DBA和系统管理员必备的参考资料,通过深入阅读,不仅可以掌握MySQL 5.1.0的基础操作,还能深入了解其高级特性和最佳实践,提升数据库管理和开发能力。Mysql_cn.chm文件作为手册...
- MySQL安装与配置:如何在不同操作系统上安装MySQL,以及配置数据库服务器。 2. **MySQL架构** - 存储引擎:MySQL支持多种存储引擎,如InnoDB(事务处理)、MyISAM(非事务处理)等,讲解各引擎特点及应用场景。...
十、最佳实践与案例分享 学习笔记可能会包含实际工作中的最佳实践和经验教训,帮助读者避免常见错误,提升工作效率。 总之,“超经典mysql dba学习笔记”是一份全面的学习资源,涵盖了MySQL DBA从入门到进阶的所有...
- 错误日志:解析和解读MySQL错误日志,定位问题所在。 - 监控工具:使用SHOW命令、performance_schema或其他监控工具分析系统状态。 9. **安全最佳实践** - 安全策略:实施最佳的安全配置,如使用SSL连接,限制...
以下是对Mysql性能优化的21个最佳实践的详细解读: 1. **优化查询缓存**:确保查询语句能被缓存,避免使用如NOW(), RAND()等会改变结果的函数,用变量替代,以便启用查询缓存。 2. **使用EXPLAIN分析查询**:在...
MySQL中文手册完全版是数据库管理员、开发者和对MySQL感兴趣的用户不可或缺的参考资料。它详尽地涵盖了MySQL数据库管理...通过深入理解和实践手册中的内容,可以更好地掌握MySQL数据库的使用,提升你的数据库管理技能。
MySQL手册文档英文版是数据库管理员、开发者和爱好者的...通过阅读和实践,不仅可以提高对MySQL的理解,还能提升英语阅读能力,对专业发展大有裨益。在简书等平台上,还可以找到许多中文译文和解读,以辅助理解和应用。
10. **性能监控与调优**:分析MySQL性能的方法,如使用SHOW STATUS和EXPLAIN命令,以及如何调整系统参数以提高性能。 《mySql中文手册》通常会提供详细的API参考和语法指南,帮助开发者深入理解MySQL的每一个功能和...
**影响结果集的解读**则涉及如何通过EXPLAIN命令分析查询执行计划,理解查询的执行步骤和资源消耗。 **常见案例及优化思路**,例如,当遇到大量数据的JOIN操作导致性能下降时,可以考虑使用物化视图、拆分大表或...
14. **错误处理和诊断**:当遇到问题时,如何解读错误代码,使用SHOW命令和EXPLAIN来分析和解决性能问题。 15. **应用程序接口(API)**:如何使用不同的编程语言(如PHP、Java、Python等)连接和操作MySQL数据库,...
1. **安装与配置**:在开始管理MySQL之前,你需要了解如何在不同操作系统上安装MySQL服务器,包括Windows、Linux和macOS。配置文件(如my.cnf)的修改也很关键,它决定了数据库的运行参数,如最大连接数、内存使用等...
书中介绍了如何设置基准测试环境、编写测试脚本以及解读测试结果。 通过阅读《高性能MySQL(第3版)》,读者不仅可以掌握MySQL的高级特性和最佳实践,还能学会如何针对具体场景进行优化,从而提升数据库系统的整体...
3. **性能调优**:MySQL性能优化涉及到索引策略、查询优化、存储引擎选择(InnoDB vs MyISAM)以及分析工具的使用,如`EXPLAIN`来理解查询执行计划。调整`max_connections`和`thread_cache_size`等参数可以提高并发...
15. **错误处理和诊断**:如何解读MySQL返回的错误代码,以及如何诊断和解决常见的问题。 这个官方文档是学习和掌握MySQL 5.5的宝典,无论你是初学者还是经验丰富的开发者,都可以从中找到你需要的信息和答案。通过...