前面我们说了extra,这个主要显示额外的信息,比如如果没有填写表,会显示no table,用了索引会显示using index,全表扫描或者回表,则会显示using where,如果mysql优化器转内部查询,还会吧内部查询选择的策略显示出来,比如内部连接临时表去重复值查询,比如松散查询,比如原则的方法,循环查询。
如果我们在explain中加个 format=JSON会发生什么呢?这时候我们可以获取到json数据,
mysql> EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1, # 整个select只有一个id的意思
"cost_info": {
"query_cost": "3197.16" # 整个查询成本预计为3197.16
},
"nested_loop": [ # 几个表之间采用嵌套循环连接查询
# 以下是参与嵌套循环连接算法的各个表的信息
{
"table": {
"table_name": "s1", # s1表是驱动表
"access_type": "ALL", # 访问方法为ALL,意味着使用全表扫描访问
"possible_keys": [ # 可能使用的索引
"idx_key1"
],
"rows_examined_per_scan": 9688, # 查询一次s1表大致需要扫描9688条记录
"rows_produced_per_join": 968, # 驱动表s1的扇出是968
"filtered": "10.00", # condition filtering代表的百分比
"cost_info": {
"read_cost": "1840.84", # 稍后解释
"eval_cost": "193.76", # 稍后解释
"prefix_cost": "2034.60", # 单次查询s1表总共的成本
"data_read_per_join": "1M" # 读取的数据量
},
"used_columns": [ # 执行查询中涉及到的列
"id",
"key1",
"key2",
"key3",
"key_part1",
"key_part2",
"key_part3",
"common_field"
],
# 对s1表访问时针对单表查询的条件
"attached_condition": "((`xiaohaizi`.`s1`.`common_field` = 'a') and (`xiaohaizi`.`s1`.`key1` is not null))"
}
},
{
"table": {
"table_name": "s2", # s2表是被驱动表
"access_type": "ref", # 访问方法为ref,意味着使用索引等值匹配的方式访问
"possible_keys": [ # 可能使用的索引
"idx_key2"
],
"key": "idx_key2", # 实际使用的索引
"used_key_parts": [ # 使用到的索引列
"key2"
],
"key_length": "5", # key_len
"ref": [ # 与key2列进行等值匹配的对象
"xiaohaizi.s1.key1"
],
"rows_examined_per_scan": 1, # 查询一次s2表大致需要扫描1条记录
"rows_produced_per_join": 968, # 被驱动表s2的扇出是968(由于后边没有多余的表进行连接,所以这个值也没啥用)
"filtered": "100.00", # condition filtering代表的百分比
# s2表使用索引进行查询的搜索条件
"index_condition": "(`xiaohaizi`.`s1`.`key1` = `xiaohaizi`.`s2`.`key2`)",
"cost_info": {
"read_cost": "968.80", # 稍后解释
"eval_cost": "193.76", # 稍后解释
"prefix_cost": "3197.16", # 单词查询s1,多次查询s2总共的成本
"data_read_per_join": "1M" # 读取的数据量
},
"used_columns": [ # 执行查询中涉及到的列
"id",
"key1",
"key2",
"key3",
"key_part1",
"key_part2",
"key_part3",
"common_field"
]
}
}
]
}
}
1 row in set, 2 warnings (0.00 sec)
我们先看一下驱动表S1的cost_info
这里面的read_cost由两部分组成
1、是I/O成本
2、检测rows * (1 - filter) 条的记录成本(rows就是我们前面执行计划的输出列,rows在json数据里显示的就是rows_examined_per_scan,filtered不变)
eval_cost:检测rows * filter成本记录。
prefix_cost就是单独查询s1表的成本 : read_cost + eval_cost。
data_read_per_join就代表读取的数量大小。
S2的cost_info其实和s1差不多,但是因为被驱动表是有机会被访问多次的,所以read_cost 和 eval_cost是访问多次累计的,而prefix_cost是s1的成本和s2的成本加起来总数。
Extented Explain
另外,当我们用完explain之后,紧接着还可以使用show warnings 还可以看到一些扩展信息。
mysql> EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.common_field IS NOT NULL;
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE | s2 | NULL | ALL | idx_key1 | NULL | NULL | NULL | 9954 | 90.00 | Using where |
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s2.key1 | 1 | 100.00 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select `xiaohaizi`.`s1`.`key1` AS `key1`,`xiaohaizi`.`s2`.`key1` AS `key1` from `xiaohaizi`.`s1` join `xiaohaizi`.`s2` where ((`xiaohaizi`.`s1`.`key1` = `xiaohaizi`.`s2`.`key1`) and (`xiaohaizi`.`s2`.`common_field` is not null))
1 row in set (0.00 sec)
这里有三个字段,level,code,message
code为1003时,message表示sql优化之后的语句,比如in转成半连接,左连接转成内连接等等。(注意,这里message只是参考,不可以直接拿来运行)
文章来源:知乎平台 原文地址:https://zhuanlan.zhihu.com/p/415644930