Mysql-Explain(八):输出列-extra

简介

extra包含其explain字段不适合显示但又十分重要的额外信息using filesort使用文件内排序
using tmporary使用临时表保存中间结果,常见于排序order by和分组group by
Using index表示覆盖索引即可满足查询要求,因而无需再回表查询
Using index for group by读取和分组都使用了覆盖索引
Using whereServer层对存储引擎层返回的数据做where条件过滤
impossiable wherewhere的值总fasle,不能获取任何记录
Using join buffer联表查询时使用的缓存策略,有Block Nested-Loop Join和Batched Key Access两种策略
Select tables optimized away在没有group by子句的情况下,基于索引优化的MAX/MIN操作,或者基于MyISAM存储引擎优化的COUNT(*)操作,不必等到执行阶段再进行计算,在查询计划生成阶段既可以完成优化
Distinct优化Distinct操作,在找到匹配的第一行记录后,立马停止查找同样的值

演示

  • using filesort:使用文件内排序

    mysql> explain select * from student where school_id = 1 order by name;
    +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+----------------+
    | id | select_type | table   | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra          |
    +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+----------------+
    |  1 | SIMPLE      | student | NULL       | ref  | ik_schoolId_majorId | ik_schoolId_majorId | 5       | const | 1960 |   100.00 | Using filesort |
    +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+----------------+
    1 row in set, 1 warning (0.00 sec)
    

    ik_schoolId_majorId只能用于查询,无帮助name字段完成排序,所以MySQL需要借助临时文件来完成排序。Using filesort一般在是我们不太愿意看见的,我们写Sql语句的时候要尽量避免出现文件内排序。

    mysql> explain select count(*) as count,school_id from student group by school_id order by count;
    +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+
    | id | select_type | table   | partitions | type  | possible_keys       | key                 | key_len | ref  | rows    | filtered | Extra                                        |
    +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+
    |  1 | SIMPLE      | student | NULL       | index | ik_schoolId_majorId | ik_schoolId_majorId | 10      | NULL | 1994142 |   100.00 | Using index; Using temporary; Using filesort |
    +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+
    1 row in set, 1 warning (0.00 sec)
    

    当然我们无法说有Using filesort的Sql语句就一定有问题,而且有些时候也无法完全消除Using filesort。
    例如上面例子中的语句,需要对分组统计的数量进行排序,这个就无法通过索引来完成排序,必须通过文件内排序来完成。如果Using filesort真的就那么十恶不赦的话,Mysql早就将它清除了。但是我们还是必须要将文件类排序的消耗降到最低,尽量多用索引,尽量减少需要排序的行数。

  • using tmporary:使用临时表保存中间结果,常见于排序order by和分组group by

    mysql> explain select count(*) as count,school_id from student group by school_id order by count;
    +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+
    | id | select_type | table   | partitions | type  | possible_keys       | key                 | key_len | ref  | rows    | filtered | Extra                                        |
    +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+
    |  1 | SIMPLE      | student | NULL       | index | ik_schoolId_majorId | ik_schoolId_majorId | 10      | NULL | 1994142 |   100.00 | Using index; Using temporary; Using filesort |
    +----+-------------+---------+------------+-------+---------------------+---------------------+---------+------+---------+----------+----------------------------------------------+
    1 row in set, 1 warning (0.00 sec)
    

    还是上面的结果,MySQL使用临时表保持了分组统计的结果,再对结果进行了排序。using tmporary和Using filesort一样不是一个我们乐意见到的东西,因此和对待Using filesort一样,可以避免就尽量避免,如果无法避免,就尽量将临时表的记录行数降到最少,减少其开销。而且实在不行了,有时候可以通过程序层面去消除它,不一定非要纠结在MySQL语句上,比如把一个很长的语句分成几个。

  • Using index:表示覆盖索引即可满足查询要求,因而无需再回表查询。

    mysql> explain select id,school_id,major_id from student;
    +----+-------------+---------+------------+-------+---------------+---------------------+---------+------+---------+----------+-------------+
    | id | select_type | table   | partitions | type  | possible_keys | key                 | key_len | ref  | rows    | filtered | Extra       |
    +----+-------------+---------+------------+-------+---------------+---------------------+---------+------+---------+----------+-------------+
    |  1 | SIMPLE      | student | NULL       | index | NULL          | ik_schoolId_majorId | 10      | NULL | 1994142 |   100.00 | Using index |
    +----+-------------+---------+------------+-------+---------------+---------------------+---------+------+---------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
  • Using where:表示MySQL将对存储引擎层提取的结果进行过滤,它表示的是Server层对存储引擎层返回的数据所做的过滤。

    mysql> explain select * from student where school_id = 1 and name like '%N%';
    +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
    | id | select_type | table   | partitions | type | possible_keys       | key                 | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
    |  1 | SIMPLE      | student | NULL       | ref  | ik_schoolId_majorId | ik_schoolId_majorId | 5       | const | 1960 |    11.11 | Using where |
    +----+-------------+---------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    

    索引ik_schoolId_majorId在存储引擎层仅仅能完成shcool_id的条件读取,然后存储引擎返回结果到Server层,然后Server层再对数据进行name条件的筛选。

  • Using index condition:使用了Index Condition Pushdown (IPC) ,这是Mysql 5.6开始支持的一种根据索引进行查询的优化方式。其优化支持range、ref、eq_ref、ref_or_null类型的查询,当前支持MyISAM和InnoDB存储引擎。

    mysql> explain select * from student where school_id < 5 and major_id < 5;
    +----+-------------+---------+------------+-------+--------------------------------+---------------------+---------+------+-------+----------+-----------------------+
    | id | select_type | table   | partitions | type  | possible_keys                  | key                 | key_len | ref  | rows  | filtered | Extra                 |
    +----+-------------+---------+------------+-------+--------------------------------+---------------------+---------+------+-------+----------+-----------------------+
    |  1 | SIMPLE      | student | NULL       | range | ik_schoolId_majorId,ik_majorId | ik_schoolId_majorId | 5       | NULL | 19444 |     1.90 | Using index condition |
    +----+-------------+---------+------------+-------+--------------------------------+---------------------+---------+------+-------+----------+-----------------------+
    1 row in set, 1 warning (0.00 sec)
    

    根据Mysql的最左原则,“where school_id < 5 and major_id < 5”,索引k_schoolId_majorId 只有第一个字段school_id起作用,按照上面Using where的说法,应该是存储层返回符合“ school_id < 5 ”条件的数据到server层,然后再对“major_id < 5”条件进行匹配,然而extra并没有出现Using where。

    这是因为Mysql使用了IPC,在上面的例子中虽然无法直接通过索引读取所需的记录,但是where条件都在ik_schoolId_majorId中,因此Mysql在读取符合“ school_id < 5 ”条件的索引结果后,并没有利用索引结果直接去读取源表,然后返回server层进行第二次的筛选。而是直接利用索引树再对上面索引结果进行一次筛选,然后再去读取源表数据,从而减少了对源表读取消耗。

    在上面Using where的例子中无法使用IPC,那是因为name条件不是索引ik_schoolId_majorId所包含的条件,无法利用索引树进行筛选。

  • Using join buffer:联表查询缓存。
    Block Nested-Loop Join算法:将外层循环的行/结果集存入join buffer, 内层循环的每一行与整个buffer中的记录做比较,从而减少内层循环的次数。优化器管理参数optimizer_switch中中的block_nested_loop参数控制着BNL是否被用于优化器。默认条件下是开启,若果设置为off,优化器在选择 join方式的时候会选择NLJ(Nested Loop Join)算法。
    Batched Key Access原理:对于多表join语句,当MySQL使用索引访问第二个join表的时候,使用一个join buffer来收集第一个操作对象生成的相关列值。BKA构建好key后,批量传给引擎层做索引查找。key是通过MRR接口提交给引擎的(mrr目的是较正顺序)MRR使得查询更有效率,要使用BKA,必须调整系统参数optimizer_switch的值,batched_key_access设置为on,因为BKA使用了MRR,因此也要打开MRR。

  • impossiable where:where的值总fasle,不能获取任何记录

    mysql> explain select * from student where name = 'NnbtPQLjMf' and name = 'aa';
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra            |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
    |  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Impossible WHERE |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
    1 row in set, 1 warning (0.00 sec)
    
  • Select tables optimized away:在没有group by子句的情况下,基于索引优化的MAX/MIN操作,或者基于MyISAM存储引擎优化的COUNT(*)操作,不必等到执行阶段再进行计算,在查询计划生成阶段既可以完成优化

    mysql> explain select max(school_id) from student;
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                        |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
    |  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Select tables optimized away |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
    1 row in set, 1 warning (0.02 sec)
    
  • Distinct:优化Distinct操作,在找到匹配的第一行记录后,立马停止查找同样的值

    mysql> explain select distinct major_id from student left join school on school.id = student.school_id where major_id = 1;
    +----+-------------+---------+------------+--------+--------------------------------+------------+---------+------------------------+------+----------+-----------------------+
    | id | select_type | table   | partitions | type   | possible_keys                  | key        | key_len | ref                    | rows | filtered | Extra                 |
    +----+-------------+---------+------------+--------+--------------------------------+------------+---------+------------------------+------+----------+-----------------------+
    |  1 | SIMPLE      | student | NULL       | ref    | ik_schoolId_majorId,ik_majorId | ik_majorId | 5       | const                  | 4027 |   100.00 | Using temporary       |
    |  1 | SIMPLE      | school  | NULL       | eq_ref | PRIMARY                        | PRIMARY    | 4       | mydb.student.school_id |    1 |   100.00 | Using index; Distinct |
    +----+-------------+---------+------------+--------+--------------------------------+------------+---------+------------------------+------+----------+-----------------------+
    2 rows in set, 1 warning (0.00 sec)
    

    查询所有有专业1(major_id=1)的学校Id,由于有多个学生是同一个学校同一个专业的,但是对于查询而言只需要查询到一条记录即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值