
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

前面几期我们讲过,EXPLAIN 可以得到一条 SQL 语句的执行计划,获得以下问题的参考数据:
但是 EXPLAIN 的结果相对比较单一,在可读性差的场景,则需要 DBA 结合自己的经验值提供参考。比如,输出结果中就不包含 SQL 的执行成本数据。
针对这样的困扰,我们可以使用 EXPLAIN 语句提供的 FROMAT 输出格式选项,来设置不同的输出格式,每种格式有自己独特的内容,这样就可以弥补传统执行计划结果输出单一的缺点。
目前 MySQL 支持的三种 EXPLAIN 输出格式:
本篇我们来解读 JSON 格式的执行计划输出结果。
下面是一条 SQL 语句的 JSON 格式执行计划输出结果,可以得到如下详细的输出数据:
示例:JSON 格式执行计划输出
mysql:ytt>desc format=json select count(log_date) from t2 group by log_date\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "40211.75"
},
"grouping_operation": {
"using_filesort": false,
"table": {
"table_name": "t2",
"access_type": "index",
"possible_keys": [
"idx_log_date"
],
"key": "idx_log_date",
"used_key_parts": [
"log_date"
],
"key_length": "4",
"rows_examined_per_scan": 398830,
"rows_produced_per_join": 398830,
"filtered": "100.00",
"using_index": true,
"cost_info": {
"read_cost": "328.75",
"eval_cost": "39883.00",
"prefix_cost": "40211.75",
"data_read_per_join": "316M"
},
"used_columns": [
"id",
"log_date"
]
}
}
}
}
1 row in set, 1 warning (0.00 sec)
对于传统执行计划输出,需要多次对比结果才能得到优化是否有效,而 JSON 格式执行计划除了传统执行计划提供的信息外,在对一些简单的 SQL 优化时,只需要对比成本即可了解优化是否有效。
示例:两条 SQL 的执行成本对比
SQL1: select count(*) from t1 where r1 in (select r1 from t1);
SQL2: select count(*) from t1 where 1;
这两条 SQL 都是对表 t1 求总记录数,相对简单。要判断哪条更优,只需要看成本信息即可。
mysql:ytt>pager grep -i "query_cost";
PAGER setto'grep -i "query_cost"'
mysql:ytt>descformat=jsonselectcount(*) from t1 \G
"query_cost": "199210.09"
1rowinset, 1warning (0.00 sec)
mysql:ytt>descformat=jsonselectcount(*) from t1 where r1 in (select r1 from t1)\G
"query_cost": "781454.78"
1rowinset, 1warning (0.00 sec)
从上面执行计划结果的筛选数据可以看出来,SQL1 的执行成本要低于 SQL2,所以 SQL1 要优于 SQL2。
从下面执行时间上来看,也是同样的预期。
mysql:ytt>select count(*) from t1;
+----------+
| count(*) |
+----------+
| 2560000 |
+----------+
1 row in set (0.26 sec)
mysql:ytt>selectcount(*) from t1 where r1 in (select r1 from t1);
+----------+
| count(*) |
+----------+
| 2560000 |
+----------+
1 row in set (1.89 sec)
SQL3:select count(*) from t1 a join t1 b on a.r1 = b.r1;
SQL3 也即简单的两表关联,不过有两个需要优化的点:
r1 在表 t1 里面的 NDV 值为 100。以上两点导致这条 SQL 必定效率很差,来看下传统执行计划结果:
mysql:ytt>desc select count(*) from t1 a join t1 b on a.r1 = b.r1\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: a
partitions: NULL
type: index
possible_keys: idx_r1
key: idx_r1
key_len: 5
ref: NULL
rows: 2552706
filtered: 100.00
Extra: Usingwhere; Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: b
partitions: NULL
type: ref
possible_keys: idx_r1
key: idx_r1
key_len: 5
ref: ytt.a.r1
rows: 25527
filtered: 100.00
Extra: Using index
2 rows in set, 1warning (0.00 sec)
可以得到以下几点有用信息:
再来看下 JSON 格式的执行计划:
mysql:ytt>desc format=json select count(*) from t1 a join t1 b on a.r1 = b.r1\G
*************************** 1.row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "6575362199.56"
},
"nested_loop": [
{
"table": {
"table_name": "a",
"access_type": "index",
"possible_keys": [
"idx_r1"
],
"key": "idx_r1",
"used_key_parts": [
"r1"
],
"key_length": "5",
"rows_examined_per_scan": 2552706,
"rows_produced_per_join": 2552706,
"filtered": "100.00",
"using_index": true,
"cost_info": {
"read_cost": "9762.97",
"eval_cost": "255270.60",
"prefix_cost": "265033.57",
"data_read_per_join": "77M"
},
"used_columns": [
"r1"
],
"attached_condition": "(`ytt`.`a`.`r1` is not null)"
}
},
{
"table": {
"table_name": "b",
"access_type": "ref",
"possible_keys": [
"idx_r1"
],
"key": "idx_r1",
"used_key_parts": [
"r1"
],
"key_length": "5",
"ref": [
"ytt.a.r1"
],
"rows_examined_per_scan": 25527,
"rows_produced_per_join": 65163080620,
"filtered": "100.00",
"using_index": true,
"cost_info": {
"read_cost": "58789103.96",
"eval_cost": "6516308062.04",
"prefix_cost": "6575362199.56",
"data_read_per_join": "1T"
},
"used_columns": [
"r1"
]
}
}
]
}
}
1rowinset, 1warning (0.00 sec)
从 JSON 格式执行计划结果,看到额外的数据:
r1。从 JSON 格式的执行计划可以更加清晰的看到,这条 SQL 性能巨差。
所以对于以上 SQL3 的优化有以下两点建议: