MySQL使用explain命令查看与分析索引的使用情况
作者:睿思达DBA_WGX
这篇文章主要介绍了MySQL使用explain命令查看与分析索引的使用情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
在查询语句中使用 explain 关键字,可以查看索引是否正在被使用,有没有做全表扫描,并且输出使用的索引信息。
语法格式如下:
explain select 语句;
一、数据准备
有一个 emp 表,表中的索引信息如下:
mysql> show index from emp; +-------+------------+--------------+--------------+-------------+----------- | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation +-------+------------+--------------+--------------+-------------+----------- | emp | 0 | PRIMARY | 1 | e_id | A | emp | 0 | uq_idx_phone | 1 | phone | A | emp | 1 | dept_id | 1 | dept_id | A | emp | 1 | idx_ename | 1 | e_name | A | emp | 1 | idx_addr | 1 | addr | A +-------+------------+--------------+--------------+-------------+----------- 5 rows in set (0.00 sec)
二、使用 explain 分析查询
执行以下命令:
mysql> explain select * from emp where e_name='Mark'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ref possible_keys: idx_ename key: idx_ename key_len: 81 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
查询结果中的各个选项的含义如下:
(1) id:SELECT 识别符 (2) select_type:SELECT 查询的类型 (3) table:数据表的名字 (4) partitions:匹配的分区 (5) type:访问表的方式 (6) possible_keys:查询时可能使用的索引 (7) key:实际使用的索引 (8) key_len:索引字段的长度 (9) ref:连接查询时,用于显示关联的字段 (10) rows:需要扫描的行数(估算的行数) (11) filtered:按条件过滤后查询到的记录的百分比 (12) Extra:执行情况的描述和说明
三、explain 各个选项的详细说明及举例
1、id
SELECT 识别符,是SELECT 查询的序列号。
表示查询中执行 select 子句或操作表的顺序,id 相同,执行顺序从上到下,id 不同,id 值越大则执行的优先级越高。
例如:
mysql> explain select * from emp where dept_id=(select dept_id from dept where dept_name='财务部')\G --如果包含子查询,id 的序号会递增,id 值越大执行优先级越高 *************************** 1. row *************************** id: 1 ---外部查询 select_type: PRIMARY table: emp *************************** 2. row *************************** id: 2 ---子查询 select_type: SUBQUERY --id相同,执行顺序从上到下 mysql> explain select * from emp,dept where emp.dept_id=dept.dept_id\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: dept *************************** 2. row *************************** id: 1 select_type: SIMPLE table: emp
2、select_type
表示查询中每个 select 子句的类型。有以下几种类型: (1) SIMPLE(简单的 select 查询,查询中不包含子查询或 union 查询) 例如: mysql> explain select * from dept\G *************************** 1. row *************************** id: 1 select_type: SIMPLE (2) PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的 select 被标记为 PRIMARY) 例如: mysql> explain select * from emp where dept_id=(select dept_id from dept where dept_name='财务部')\G *************************** 1. row *************************** id: 1 ---外部查询 select_type: PRIMARY table: emp *************************** 2. row *************************** id: 2 ---子查询 select_type: SUBQUERY (3) SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询) 例如:见上一个例子。 (4) DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询) 例如: mysql> explain select * from dept where exists (select * from emp where emp.dept_id=dept.dept_id)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY (5) UNION(UNION中的第二个或后面的SELECT语句) 例如: mysql> explain select * from emp where dept_id=11 union select * from emp where dept_id=22\G *************************** 1. row *************************** id: 1 select_type: PRIMARY *************************** 2. row *************************** id: 2 select_type: UNION *************************** 3. row *************************** id: NULL select_type: UNION RESULT (6) UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select) 例如:见上一个例子
3、table
查询所用的表名称,可能是别名。例如:
mysql> explain select * from emp e,dept d where e.dept_id=d.dept_id\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: d *************************** 2. row *************************** id: 1 select_type: SIMPLE table: e mysql> explain select * from emp\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp
4、partitions
查询匹配的分区:例子略。
5、type
访问表的方式,表示 MySQL 在表中找到所需行的方式。
常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)。
例如:
查询用到的表及索引情况如下:
mysql> show index from dept; +-------+------------+----------+--------------+-------------+----------- | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation +-------+------------+----------+--------------+-------------+----------- | dept | 0 | PRIMARY | 1 | dept_id | A +-------+------------+----------+--------------+-------------+----------- 1 row in set (0.00 sec) mysql> show index from emp; +-------+------------+--------------+--------------+-------------+----------- | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation +-------+------------+--------------+--------------+-------------+----------- | emp | 0 | PRIMARY | 1 | e_id | A | emp | 0 | uq_idx_phone | 1 | phone | A | emp | 1 | dept_id | 1 | dept_id | A | emp | 1 | idx_ename | 1 | e_name | A | emp | 1 | idx_addr | 1 | addr | A +-------+------------+--------------+--------------+-------------+----------- 5 rows in set (0.00 sec)
常用的类型的说明及举例:
(1)ALL:Full Table Scan,如果查询没有使用索引,MySQL将遍历全表以找到匹配的行。 例如: mysql> explain select * from emp where birth='1998-1-1'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL --该查询的条件没有创建索引,因此是全表扫描。 (2)index: 全索引扫描,和 ALL 相比,index 只遍历索引树,通常比 ALL 快,因为索引文件通常比数据文件小。 例如: mysql> explain select e_id from emp\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: index --该查询是把 e_id 列中所有数据全部取出,并且对 e_id 列创建了索引,因此需要遍历整个索引树 (3)range:检索给定范围的行,可以在 key 列中查看使用的索引,一般出现在 where 语句的条件中,如使用between、>、<、in等查询。 例如: mysql> explain select * from emp where e_id>1000\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: range (4)ref: 非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回匹配某条件的多行值。 例如: mysql> explain select * from emp where dept_id=11\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ref --该查询针对 dept_id 字段进行查询,查询到多条记录,并且为 dept_id 字段创建了索引。 (5)eq_ref: 唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见主键或唯一索引扫描。 mysql> explain select * from emp join dept on emp.dept_id=dept.dept_id where e_name='Jack'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ref *************************** 2. row *************************** id: 1 select_type: SIMPLE table: dept partitions: NULL type: eq_ref (6)const: 表示通过一次索引就找到了结果,常出现于 primary key 或 unique 索引。因为只匹配一行数据,所以查询非常快。如将主键置于 where 条件中,MySQL 就能将查询转换为一个常量。 例如: mysql> explain select * from emp where e_id=1002\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: const (7)NULL: 执行时不用访问表或索引。 例如: mysql> explain select 1 from dual\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: NULL partitions: NULL type: NULL
6、possible_keys
显示可能应用在表中的索引,可能一个或多个。
查询涉及到的字段若存在索引,则该索引将被列出,但不一定被查询实际使用。
例如:
mysql> explain select * from emp join dept on emp.dept_id=dept.dept_id where e_name='Jack'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ref possible_keys: dept_id,idx_ename key: idx_ename key_len: 81 ref: const rows: 1 filtered: 100.00 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table: dept partitions: NULL type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: wanggx.emp.dept_id rows: 1 filtered: 100.00 Extra: NULL 2 rows in set, 1 warning (0.00 sec)
7、key
实际使用的索引,如为NULL,则表示未使用索引。
若查询中使用了覆盖索引,则该索引和查询的 select 字段重叠。
见上一个例子。
8、key_len
表示索引所使用的字节数,可通过该列计算查询中使用的索引长度。
在不损失精确性的情况下,长度越短越好。
例如:
mysql> explain select * from emp where e_id=1001\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec) --索引长度为4,因为主键时整型,长度为4 mysql> explain select * from emp where phone='13037316644'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: const possible_keys: uq_idx_phone key: uq_idx_phone key_len: 81 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
9、ref
显示关联的字段,如果是非连接查询,则显示 const,如果是连接查询,则会显示关联的字段。
例如:
mysql> explain select * from emp join dept on emp.dept_id=dept.dept_id where e_name='Jack'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ref possible_keys: dept_id,idx_ename key: idx_ename key_len: 81 ref: const *************************** 2. row *************************** id: 1 select_type: SIMPLE table: dept partitions: NULL type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: wanggx.emp.dept_id
10、rows
根据表统计信息及索引选用情况大致估算出找到所需记录所要读取的行数。
当然该值越小越好。
mysql> explain select * from emp where salary=5000\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 filtered: 33.33 Extra: Using where 1 row in set, 1 warning (0.00 sec) --没有使用索引,需要进行全表扫描,一共读取3行 mysql> explain select * from emp where dept_id=11\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ref possible_keys: dept_id key: dept_id key_len: 5 ref: const rows: 2 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec) --使用了索引,扫描2行
11、filtered
表示选取的行和读取的行的百分比,100表示选取了100%,80表示读取了80%。
例如:
mysql> explain select * from emp where phone = '13703735488'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: const possible_keys: uq_idx_phone key: uq_idx_phone key_len: 81 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.01 sec) --根据phone创建了唯一索引,并且条件是等号(=),因此filtered为100% mysql> explain select * from emp where salary = 5200\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 50.00 Extra: Using where 1 row in set, 1 warning (0.00 sec) --由于salary字段没有创建索引,因此执行全表扫描,filtered为50%
12、extra
显示一些重要的额外信息。一般有以下几项:
(1)Using filesort:对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”。 例如: mysql> explain select * from emp order by salary\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: Using filesort 1 row in set, 1 warning (0.01 sec) (2)Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by 与 order by。 例如: mysql> explain select count(*) from emp group by salary\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: Using temporary; Using filesort 1 row in set, 1 warning (0.00 sec) (3)Using index:表明相应的select操作中使用了覆盖索引(select的数据列只从索引中就能取得数据,不必读取数据行)。 mysql> explain select e_name,salary from emp order by e_name\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: Using filesort 1 row in set, 1 warning (0.00 sec) mysql> explain select e_name from emp order by e_name\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: index possible_keys: NULL key: idx_ename key_len: 81 ref: NULL rows: 2 filtered: 100.00 Extra: Using index 1 row in set, 1 warning (0.00 sec) (4)Using join buffer:表明在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,应注意根据查询的具体情况可能需要添加索引来改进能。 例如: mysql> explain select * from emp join dept on emp.dept_id=dept.dept_id\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: dept partitions: NULL type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: SIMPLE table: emp partitions: NULL type: ALL possible_keys: dept_id key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: Using where; Using join buffer (Block Nested Loop) 2 rows in set, 1 warning (0.00 sec)
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。