【MYSQL】EXPLAIN Json格式的执行计划

1,068 阅读3分钟

EXPLAIN单词和真正的查询语句中间加上FORMAT=JSON

mysql> EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\G

{
	"query_block": {
		"select_id": 1,
		#整个查询语句只有1个SELECT关键字, 该关键字对应的id号为1 "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"]
			}
		}]
	}
}

我们使用#后边跟随注释的形式为大家解释了EXPLAIN FORMAT=JSON语句的输出内容,但是大家可能有疑问"cost_info"里边的成本看着怪怪的,它们是怎么计算出来的?先看s1表的"cost_info"部分:

"cost_info": {
    "read_cost": "1840.84",
    "eval_cost": "193.76",
    "prefix_cost": "2034.60",
    "data_read_per_join": "1M"
}
  • read_cost是由下边这两部分组成的:

    • IO成本

    • 检测rows × (1 - filter)条记录的CPU成本

    小贴士: rows和filter都是我们前边介绍执行计划的输出列,在JSON格式的执行计划中,rows相当于rows_examined_per_scan,filtered名称不变。

  • eval_cost是这样计算的:

    检测 rows × filter条记录的成本。

  • prefix_cost就是单独查询s1表的成本,也就是:

    read_cost + eval_cost

  • data_read_per_join表示在此次查询中需要读取的数据量,我们就不多唠叨这个了。

小贴士: 大家其实没必要关注MySQL为啥使用这么古怪的方式计算出read_cost和eval_cost,关注prefix_cost是查询s1表的成本就好了。

对于s2表的"cost_info"部分是这样的:

"cost_info": {
    "read_cost": "968.80",
    "eval_cost": "193.76",
    "prefix_cost": "3197.16",
    "data_read_per_join": "1M"
}

由于s2表是被驱动表,所以可能被读取多次,这里的read_costeval_cost是访问多次s2表后累加起来的值,大家主要关注里边儿的prefix_cost的值代表的是整个连接查询预计的成本,也就是单次查询s1表和多次查询s2表后的成本的和,也就是:

968.80 + 193.76 + 2034.60 = 3197.16

\