MYSQL 中的EXPLAIN 语句可以用来分析查询语句,分析语句的执行情况。
基本语法是:
EXPLAIN SELECT select_options
结果简述:
字段 | 取值和含义 |
---|---|
id | select 标识符,查询序号表示执行的优先级 |
select_type | 表示SELECT语句的类型【SIMPLE,PRIMARY,UNION,UNION RESULT,…】 |
table | 查询的哪张表 |
partitions | |
type | 表示表的连接类型【system,const,eq_ref…】 |
possible_keys | 可以使用哪个索引来进行查询,NULL则表示没有用到索引 |
key | 实际查询用到的索引,NULL则表示没有用到索引 |
key_len | 表示索引字段的字节长度 |
ref | 表示使用哪个列或常数与索引一起来查询记录 |
rows | 查询是需要扫描的行数 |
filtered | |
Extra | 处理查询时的详细信息【using index ,using where,using temporary,using filesort】 |
示例,已下面这张表为例
mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
mysql> select count(1) from employees;
+----------+
| count(1) |
+----------+
| 300024 |
+----------+
1 row in set (0.18 sec)
mysql> show index from employees;
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | emp_no | A | 299423 | NULL | NULL | | BTREE | | |
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)
mysql> explain select * from employees where emp_no=10001;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.38 sec)
mysql> explain select * from employees where first_name='Georgi' and last_name='Facello';
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 299423 | 1.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
从以上两个EXPLAIN来看结果分析:
语句1:select * from employees where emp_no=10001
一个简单查询,使用了主键索引,扫描行数为1
语句2:select * from employees where first_name=‘Georgi’ and last_name=‘Facello’
一个简单查询,未使用索引,做了全表扫描
针对查询2的优化 可以添加一个联合索引
分析联合索引的离散度
mysql> select count(distinct(concat(first_name,last_name))) from employees;
+-----------------------------------------------+
| count(distinct(concat(first_name,last_name))) |
+-----------------------------------------------+
| 279408 |
+-----------------------------------------------+
1 row in set (0.58 sec)
这两个字段做联合后离散度很高,适合添加索引
mysql> alter table employees add index index_full_name(first_name,last_name);
Query OK, 0 rows affected (3.97 sec)
Records: 0 Duplicates: 0 Warnings: 0
再分析语句2
mysql> explain select * from employees where first_name='Georgi' and last_name='Facello';
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | ref | index_full_name | index_full_name | 94 | const,const | 2 | 100.00 | NULL |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
使用了索引,只扫描2行,效率提高
如果变更条件
使用了索引,扫描253行数据
mysql> explain select * from employees where first_name='Georgi' ;
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | ref | index_full_name | index_full_name | 44 | const | 253 | 100.00 | NULL |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
未使用索引,不满足最左匹配原则
mysql> explain select * from employees where last_name='Facello' ;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 299423 | 10.00 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
不满足最左匹配原则,但是使用了覆盖索引,因结果集数据存在于index_full_name ,不需要回表
mysql> explain select first_name,last_name from employees where last_name='Facello' ;
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+--------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+--------+----------+--------------------------+
| 1 | SIMPLE | employees | NULL | index | NULL | index_full_name | 94 | NULL | 299423 | 10.00 | Using where; Using index |
+----+-------------+-----------+------------+-------+---------------+-----------------+---------+------+--------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)
使用了索引,且使用了覆盖索引
mysql> explain select first_name,last_name from employees where first_name='Georgi' ;
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | employees | NULL | ref | index_full_name | index_full_name | 44 | const | 253 | 100.00 | Using index |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
看起来不满足最左匹配原则,但是优化器Query Optimizer 对下列语句做了优化,变成
select * from employees where first_name='Georgi' and last_name='Facello';
mysql> explain select * from employees where last_name='Facello' and first_name='Georgi' ;
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | ref | index_full_name | index_full_name | 94 | const,const | 2 | 100.00 | NULL |
+----+-------------+-----------+------------+------+-----------------+-----------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)
分析底层存储
#添加索引前
-rw-r----- 1 mysql mysql 8768 4月 19 21:47 employees.frm
-rw-r----- 1 mysql mysql 23068672 4月 19 21:48 employees.ibd
#添加索引后
-rw-r----- 1 mysql mysql 8768 4月 21 23:01 employees.frm
-rw-r----- 1 mysql mysql 30408704 4月 21 23:01 employees.ibd
很明显,因为添加了一个索引后,又多生成了一个B+tree,数据文件更大