Mysql-Explain(五):输出列-type

简介

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返回这个值,那就要考虑是否要优化了。

猜你喜欢

转载自blog.csdn.net/u012180773/article/details/104318904
今日推荐