mysql · 2020-01-09

MySQL的explain之type

一、准备

1.创建表

-- 班级表:cid 主键,用于 const / eq_ref
CREATE TABLE clazz (
  cid INT PRIMARY KEY,
  cname VARCHAR(20) NOT NULL
) ENGINE=InnoDB;

-- 学生表:sid 主键;cid、age 普通索引
CREATE TABLE student (
  sid INT PRIMARY KEY,
  sname VARCHAR(20) NOT NULL,
  cid INT NOT NULL,
  age INT NOT NULL,
  info VARCHAR(100),
  KEY idx_cid (cid),
  KEY idx_age (age)
) ENGINE=InnoDB;

-- 单行表:用于 system
CREATE TABLE single_row (
  id INT PRIMARY KEY,
  name VARCHAR(20)
) ENGINE=MyISAM;

2.插入数据

INSERT INTO clazz VALUES
(1,'一班'),(2,'二班'),(3,'三班');

INSERT INTO student VALUES
(1,'张三',1,18,'a'),
(2,'李四',1,19,'b'),
(3,'王五',2,20,'c'),
(4,'赵六',2,21,'d'),
(5,'钱七',3,22,'e');

INSERT INTO single_row VALUES (1,'only');

三、type类型

type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:

system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALL

常见的有:

system>const>eq_ref>ref>range>index>ALL

一般来说,得保证查询至少达到 range 级别,最好能达到 ref

1.system

system:表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计
触发条件:表只有一行,优化器直接知道结果只有一行
预期:type = system

mysql> EXPLAIN SELECT * FROM single_row;
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table      | partitions | type   | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | single_row | NULL       | system | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL  |
+----+-------------+------------+------------+--------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

2.const

const:查找条件是主键或者唯一,并且只匹配一行数据,意味着不用往下扫描了
触发条件:通过主键或唯一索引进行等值查询,最多返回一行
预期:type = const,key = PRIMARY

mysql> EXPLAIN SELECT * FROM student WHERE sid = 1;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT * FROM clazz WHERE cid = 1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | clazz | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

3.eq_ref

eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
触发条件:多表关联时,被驱动表通过主键或唯一索引等值查找,前表每一行只匹配后表一行。
预期:clazz 这一行 type = eq_ref,key = PRIMARY。

-- STRAIGHT_JOIN:强制按书写顺序连接,左表 student 一定是驱动表,右表 clazz 一定是被驱动表
mysql> EXPLAIN SELECT s.sid, s.sname, c.cname FROM student s STRAIGHT_JOIN clazz c ON s.cid = c.cid;
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref         | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | s     | NULL       | ALL    | idx_cid       | NULL    | NULL    | NULL        |    5 |   100.00 | NULL  |
|  1 | SIMPLE      | c     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test1.s.cid |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)

4.ref

ref:查找条件使用了索引,并且不是主键或唯一
触发条件:使用非唯一索引进行等值查询,可能返回多行
预期:type = ref,key = idx_cid

mysql> EXPLAIN SELECT * FROM student WHERE cid = 1;
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | student | NULL       | ref  | idx_cid       | idx_cid | 4       | const |    2 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

5.range

range:查找条件使用了索引,值是个范围(beween、>等)
触发条件:使用索引进行范围扫描,如 >、<、BETWEEN、IN 等
预期:type = range,key = PRIMARY 或 idx_age

mysql> EXPLAIN SELECT * FROM student WHERE sid BETWEEN 2 AND 4;
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL |    3 |   100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT * FROM student WHERE age > 18;
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | student | NULL       | range | idx_age       | idx_age | 4       | NULL |    4 |   100.00 | Using index condition |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

6.index

index:这种连接类型只是另外一种形式的全表扫描,只不过它的扫描是按照索引的顺序,all是沿着磁盘扫描,index是沿着索引扫描

索引覆盖:查询的值就是索引的值,不用扫描表

触发条件:扫描整个索引树,而不是全表。常见于覆盖索引查询
预期:type = index,key = idx_cid,Extra = Using index

mysql> EXPLAIN SELECT cid FROM student;
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | index | NULL          | idx_cid | 4       | NULL |    5 |   100.00 | Using index |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

7.ALL

ALL:Full Table Scan,将遍历全表以找到匹配的行
触发条件:没有可用索引,或索引失效,进行全表扫描
预期:type = ALL,key = NULL

mysql> EXPLAIN SELECT * FROM student WHERE sname = '张三';
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    5 |    20.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)