Mysql-Explain(五):输出列-type
Mysql-Explain(一):explain简介和输出列解释
Mysql-Explain(二):explain实验数据准备
Mysql-Explain(三):输出列-id
Mysql-Explain(四):输出列-select_type
Mysql-Explain(五):输出列-type
Mysql-Explain(六):输出列-possiable_keys、key、key_len
Mysql-Explain(七):输出列-ref、rows
Mysql-Explain(八):输出列-extra
简介
type | 访问类型排序,显示查询使用了何种类型,从最好到最差依次是:system>const>eq_ref>ref>range>index>ALL | system | 表只有一行记录,这是const类型的特例,这个平时很少出现 |
const | 表最多有一个匹配行,它将在查询开始时被读取。因为仅有一行记录匹配,所以这行的列值可被优化器剩余部分认为是常数。const表很快,因为它们只读取一次! | ||
eq_ref | 驱动表和关联表中的每行进行组合并且仅有一行记录。这是除了system 和 const 类型之外, 这是最好的联接类型。当连接使用索引的所有部分时, 索引是主键或唯一非 NULL 索引时, 将使用该值 | ||
ref | 非唯一性索引扫描,返回符合某个索引值的所有记录,可能会有多条记录匹配 | ||
range | 使用一个索引来检索给定范围的行,这种范围索引扫描比全表扫描效率要高 | ||
index | 使用覆盖索引 | ||
all | 全表扫描(full table scan) |
演示
-
system:表只有一行记录,这是const类型的特例
mysql> explain select * from (select * from student limit 1) s; +----+-------------+------------+------------+--------+---------------+------+---------+------+---------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+--------+---------------+------+---------+------+---------+----------+-------+ | 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL | | 2 | DERIVED | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 100.00 | NULL | +----+-------------+------------+------------+--------+---------------+------+---------+------+---------+----------+-------+ 2 rows in set, 1 warning (0.00 sec)
上图所示from列表子查询派生出的临时表<derived2>只有一行记录,因此外层临时表的select查询的type值就是system。其实这种情况在实际的应用中非常少见,上面例子的Sql语句也仅仅是为了演示这样写。
-
const:表最多有一个匹配行,它将在查询开始时被读取
mysql> explain select * from student where id = 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)
where条件是id=常量,所以肯定只有一行记录被匹配,所以type的值为const。
mysql> SELECT * FROM mydb.student where name = 'NnbtPQLjMf'; +----+------------+------+-----------+----------+ | id | name | age | school_id | major_id | +----+------------+------+-----------+----------+ | 1 | NnbtPQLjMf | 23 | 321 | 314 | +----+------------+------+-----------+----------+ 1 row in set (0.60 sec) mysql> explain select * from student where name = 'NnbtPQLjMf'; +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 10.00 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
上图所示第一条SQL语句查询结果只有一条记录,但是下面explain的结果显示type值却是all。
这里要理解清楚,const是表示mysql优化器在执行语句之前判断其结果肯定只有一条记录,因为id作为主键具有唯一性,id=常量肯定只有一条结果,因此mysql只有读取到第一条记录就可以停止查询直接返回结果。而第二个例子中,name字段既不是主键也不是唯一性索引,所以优化器无法判断其结果的数量(哪怕实际结果只是一条记录),所以要扫描所有记录。explain是sql语句执行前的优化和分析,不是执行后的统计! -
驱动表和关联表中的每行进行组合并且仅有一行记录
mysql> explain select * from student left join school on student.school_id = school.id; +----+-------------+---------+------------+--------+---------------+---------+---------+------------------------+---------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+--------+---------------+---------+---------+------------------------+---------+----------+-------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 100.00 | NULL | | 1 | SIMPLE | school | NULL | eq_ref | PRIMARY | PRIMARY | 4 | mydb.student.school_id | 1 | 100.00 | NULL | +----+-------------+---------+------------+--------+---------------+---------+---------+------------------------+---------+----------+-------+ 2 rows in set, 1 warning (0.02 sec)
以表student为驱动表关联查询表school的时候,表student和表school是一对一关系,因为student.school_id对于的是school的主键(如果是唯一索引也可以),所以一个学生只能关联一所学校,所以表student的每一行记录都只能和关联表school组合成最多一条记录,所以第二行记录中type是eq_ref,表示驱动表每行记录只需要和关联表组合到第一行记录即可,你可以理解成student表的每一行记录在关联表school都是进行了一次type=const的查询。eq_ref是一种比较理想的查询方式,是我们在性能优化中比较原因看到的结果。
-
ref:非唯一性索引扫描,返回符合某个索引值的所有记录,可能会有多条记录匹配。
mysql> ALTER TABLE `mydb`.`student` ADD INDEX `ik_name` USING BTREE (`name`) VISIBLE; Query OK, 0 rows affected (20.56 sec) Records: 0 Duplicates: 0 Warnings: 0
先创建普通索引ik_name,该索引只有name一个字段。
mysql> explain select * from student where name = 'NnbtPQLjMf'; +----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | student | NULL | ref | ik_name | ik_name | 1023 | const | 1 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+---------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
单表查询,表game的type_id字段是普通索引,所以优化器判断其可能会有多条匹配的记录,所以type=ref,会读取所有匹配的记录,而不会像const一样碰到第一行记录匹配便返回。但是ref是使用索引去查询记录,比全表扫描效率还是要高出很多,所以在性能优化分析中ref也是一种比较理想的结果。
-
range:使用一个索引来检索给定范围的行,这种范围索引扫描比全表扫描效率要高。
mysql> explain select * from student where id between 1 and 10; +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | student | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 10 | 100.00 | Using where | +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.04 sec)
range和ref同样是使用了所以进行查询,但是range是范围刷选,所以效率要比ref要低,但是还是要比全表扫描效率要高,而且范围查询在实际应用中是一种很普遍的存在,因此在性能优化分析中,range也是一种比较不错的结果。
-
index:使用了覆盖索引
mysql> explain select id,name from student; +----+-------------+---------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | index | NULL | ik_name | 1023 | NULL | 1800825 | 100.00 | Using index | +----+-------------+---------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
select列表只包含id,name两个字段,而所以索引ik_name就包含这两列,所以Mysql只需要扫描索引树既可以找到所需要的所有值,不需要去扫描表。这是一种非常高效的查询方式,在性能优化分析中,是我们非常乐意看到的结果。
-
all:全表扫描
mysql> explain select * from student where age < 25; +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ | 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 1800825 | 33.33 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+---------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
扫描全表,读取数据。如果表的数据不大的情况,这是一种可以接受的情况,但是数据量增大,尤其上百万之后,那就要小心了。如果explain返回这个值,那就要考虑是否要优化了。