首页 > 数据库 >Extra(5)—mysql执行计划(五十一)

Extra(5)—mysql执行计划(五十一)

时间:2022-11-01 17:40:29浏览次数:48  
标签:Extra ref key1 五十一 key mysql NULL type s1


前面说了有type,simple表示普通查询或者连接查询,primary代表union最左边的select,union result代表union查询的临时表去重,所以union all没有去重功能,subquery代表in的子查询物化表的情况下才会出现,dependent subquery代表相关子查询,dependent union代表相关union查询,还有driverd子查询,from后面的,也需要物化,还有物化后转连接查询,这些都能看到mysql优化器是采用哪种查询方式。

Id代表select,几个select就有几个查询,如果转链接了,就只有一个id。

Type代表存储引擎查询的方式,system代表精确查询并且只有一条记录,const,唯一索引或者主键,ref,二级索引查询,ref_or_null,range,index代表联合索引但没有触发,range和ref,最后还要all。

Possible key代表可能用到的索引。

Key实际用到的索引。

Ref代表后面的过滤条件可能用到的方式,他可能是个函数,也可能是驱动表的参数。

Rows代表查询的数据,全表代表所有,索引则代表所有满足的数据。

Filtered代表数据多少满足,和rows组合可以算出去驱动表的扇出值。

​​key&key_len&ref&filtered(4)—mysql执行计划(五十)​​

Extra

顾名思义,这列就存储的是额外信息,我们可以通过额外信息准确理解mysql到底执行查询语句。

mysql> EXPLAIN SELECT 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 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 | No tables used |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

//当我们的sql语句没有表时候,extra显示的是no tables used。

mysql> EXPLAIN SELECT * FROM s1 WHERE 1 != 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| 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.01 sec)

//当过滤条件后面永远显示false,就会出现Impossible where

mysql> EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'abcdefg';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------+
| 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 | No matching min/max row |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------+
1 row in set, 1 warning (0.00 sec)

//当查询列表有max或者min,但没有查到数据时候,会显示:no matching min/max row


mysql> EXPLAIN SELECT key1 FROM s1 WHERE key1 = 'a';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | const | 8 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

//当我们在使用索引覆盖的情况下,当我们只需要用到索引,而不需要回表操作,则显示 using index。

SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%b';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | s1 | NULL | range | idx_key1 | idx_key1 | 303 | NULL | 266 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.01 sec)

//有些虽然用了索引列,但不能使用到索引
//其中key1>'z'可以使用到索引,但后面的like不可以使用索引,
//传统访问应该先索引在索引b+树查询满足z的条件,然后数据回表查询过滤满足like '%a'的数据。
//但其实可以在索引b+树查询满足z的条件后,继续在b+树过滤索引满足key1 like '%a'的索引,因为key1
//也在索引b+树,这样就减少回表的开销。而且因为回表是随机I/O,这边是顺序I/O,效率也会快很多。
//mysql吧这种就称呼 索引条件下推,using index condition

mysql> EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

//当我们全表扫描的时候,显示的是using where

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND common_field = 'a';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | const | 8 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

//当我们使用索引访问,但还有其他搜索条件还有common_Filed所以还是显示suing where

mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 10.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.03 sec)

//前面说过基于块的嵌套法,当被驱动表不能通过索引访问,这时候会有一个join buffer,来加快查询的速度,因为这个太多的话
//内存里不够存放足够多的数据,只能不断通过驱动表查询的结果,一条条从磁盘访问驱动表,
//这时候就把被驱动表的数据放在join buffer里,直接在内存中处理是否满足,就不需要每次都从磁盘查询数据。
//所以上面可以看到,因为不能使用索引扫描,所以退而求其次,用join buffer,
//又因为s2.common_field = 常数,所以这里又有一个using where

mysql> EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL;
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s1.key1 | 1 | 10.00 | Using where; Not exists |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------------------+
2 rows in set, 1 warning (0.00 sec)

//当我们在外连接,如果where条件包含被驱动表某个列等于null,但当前列有不允许为null,这时候就会显示not exists

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND key3 = 'a';
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+-------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+-------------------------------------------------+
| 1 | SIMPLE | s1 | NULL | index_merge | idx_key1,idx_key3 | idx_key3,idx_key1 | 303,303 | NULL | 1 | 100.00 | Using intersect(idx_key3,idx_key1); Using where |
+----+-------------+-------+------------+-------------+-------------------+-------------------+---------+------+------+----------+-------------------------------------------------+
1 row in set, 1 warning (0.01 sec)

//前面说过如果单独的索引,会使用合并索引,上面就显示用的using_intersect(idx_key3,idx_key1)
//如果是union则就用的是using_union合并

mysql> EXPLAIN SELECT * FROM s1 LIMIT 0;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
| 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 | Zero limit |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------+
1 row in set, 1 warning (0.00 sec)

//如果分页是0,则直接会显示zero limit

mysql> EXPLAIN SELECT * FROM s1 ORDER BY key1 LIMIT 10;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | index | NULL | idx_key1 | 303 | NULL | 10 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.03 sec

mysql> EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

//前面那个显示的null,对索引进行排序了
//但有些时候无法使用索引排序,这时候就需要从磁盘排序或者内存排序,这些都叫file sort
//如果用文件排序则就会显示using filesort

mysql> EXPLAIN SELECT DISTINCT common_field FROM s1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | Using temporary |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
1 row in set, 1 warning (0.00 sec)

//有的时候mysql会使用临时表进行去重或者排序,比如我们在distinct,group by,union等子句的查询过程,
//这时候如果不能利用索引查询,这时候会建立内部临时表,这时候就会显示 Using temporary

mysql> EXPLAIN SELECT common_field, COUNT(*) AS amount FROM s1 GROUP BY common_field;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | Using temporary; Using filesort |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT common_field, COUNT(*) AS amount FROM s1 GROUP BY common_field ORDER BY NULL;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | Using temporary |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
1 row in set, 1 warning (0.00 sec)


EXPLAIN SELECT common_field, COUNT(*) AS amount FROM s1 GROUP BY common_field ORDER BY common_field;


//上面的显示不光有using temporary 还有using filesort,也就是说不光临时表,还排序了。
//这是因为mysql默认在group by之后默认会order by,不想排序必须显示写 order by null

mysql> EXPLAIN SELECT key1, COUNT(*) AS amount FROM s1 GROUP BY key1;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | s1 | NULL | index | idx_key1 | idx_key1 | 303 | NULL | 9688 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

//另外建立临时表代价很大,能用索引 就用索引,如果用到索引group by 就会显示using index,并且索引是排序好的。


mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2 WHERE common_field = 'a');
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
| 1 | SIMPLE | s2 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9954 | 10.00 | Using where; Start temporary |
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s2.key3 | 1 | 100.00 | End temporary |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
2 rows in set, 1 warning (0.00 sec)

//当我们查询优化器吧子查询in转为semi-join的时候,有很多执行策略,其实一种也是建立临时表实现为外层查询进去重操作
//这时候驱动表start temporary 被驱动表将会显示end temporary

mysql> EXPLAIN SELECT * FROM s1 WHERE key3 IN (SELECT key1 FROM s2 WHERE key1 > 'z');
+----+-------------+-------+------------+-------+---------------+----------+---------+-------------------+------+----------+-------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+-------------------+------+----------+-------------------------------------+
| 1 | SIMPLE | s2 | NULL | range | idx_key1 | idx_key1 | 303 | NULL | 270 | 100.00 | Using where; Using index; LooseScan |
| 1 | SIMPLE | s1 | NULL | ref | idx_key3 | idx_key3 | 303 | xiaohaizi.s2.key1 | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+----------+---------+-------------------+------+----------+-------------------------------------+
2 rows in set, 1 warning (0.01 sec)

//内连接还会显示looseScan

mysql> EXPLAIN SELECT * FROM s1 WHERE common_field IN (SELECT key1 FROM s2 where s1.key3 = s2.key3);
+----+-------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-----------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9688 | 100.00 | Using where |
| 1 | SIMPLE | s2 | NULL | ref | idx_key1,idx_key3 | idx_key3 | 303 | xiaohaizi.s1.key3 | 1 | 4.87 | Using where; FirstMatch(s1) |
+----+-------------+-------+------------+------+-------------------+----------+---------+-------------------+------+----------+-----------------------------+
2 rows in set, 2 warnings (0.00 sec)

//内连接还会显示firstMatch

标签:Extra,ref,key1,五十一,key,mysql,NULL,type,s1
From: https://blog.51cto.com/u_15856702/5814518

相关文章

  • Select type&partitions (2)—mysql执行计划(四十八)
    前面说了explain的table是表名,显示在前面的代表驱动表,正常select会出现不同的id,但如果子查询本来是两个select,但被优化成连接查询,就会导致是相同的id,union查询会出现临时表,i......
  • key&key_len&ref&filtered(4)—mysql执行计划(五十)
    前面说了system是精确存储引擎和只存一条数据,const是主键和唯一索引才能达到的效率访问,ref是二级索引等值查询,或者联合索引全部等值,如果联合索引单个查询,则是index,ref_not是......
  • id,table列(1)—mysql执行计划(四十七)
    前面说了semi-join,这个是在where或者on语句后面,in里面,并且外层的条件必须用and与子查询连接,semi-join的作用就是,不管子查询有多少条数据返回,都不管,外层都只查询出来外层表数......
  • Semi-join使用条件,派生表优化 (3)—mysql基于规则优化(四十六)
    前面说了子查询里有no/any/all不能用limit,groupby,orderby等,他会被查询优化器优化掉,子查询可能会物化转成内连接semi-join查询,物化就是会吧子查询看做一个表,如果数据太大,超......
  • 子查询注意事项&semi-join(2)—mysql基于规则优化(四十五)
    前面说了mysql会吧一些冗余的sql语句查询优化重写,比如多于的括号,比如有的外连接其实跟内连接类似,可以优化查询表的顺序。子查询又分为相关和不相关子查询,如果子查询过滤条件......
  • 不重用的undo日志 (2)—mysql进阶(六十五)
    前面我们说了undo日志在insert,update,delete存储的日志格式,delete存储的type是trx_undo_del_mark_rec里面有个参数oldroll_pointer会指向insert的地址值,恢复需要的数据。Sel......
  • undo日志insert,update,delete (1)—mysql进阶(六十四)
    前面说了redo日志为了保证系统宕机的情况下,能够恢复数据,恢复数据是在以checkpoint_lsn为起始位子来恢复,在该值之前的都是已经持久化到磁盘的,可以为了提升效率而放弃,而之后的......
  • redo log(1)—mysql进阶(五十九)
    上篇文章说了我们可以用begin和statrtransaction,提交可以commit,rollback回滚,可以指定回滚到保存点,也可以设置全局变量setautocommitoff。也会隐式提交,比如开启事务后,如......
  • 【MySQL】常见错误汇总
    一、MySQL无法重新启动(cmd)netstopmysql手动关闭mysql后netstartmysql无法重启,提示服务没有报告任何错误直接上网搜启动问题的话可能解决不了,因为原因很多样。所以先......
  • redo log-Transaction(2)—mysql进阶(六十)
    前面我们说了为了吧bufferpool的数据持久化到磁盘上,比如修改了一条数据,不可能每次吧整个页的数据都刷新过去,这样耗费性能,innoDB就是把修改的数据记录在redo日志里,redo日志......