MySQL的EXPLAIN能够让开发人员了解SQL的执行路径,进而找到潜在的性能问题,以及可能优化的方向,技术社群的这篇文章《根据 EXPLAIN EXTRA 栏提示进行优化(一)》通过一些例子给我们讲解了这方面的作用。
和EXPLAIN相关的历史文章如下,
默认的 EXPLAIN 结果里有一个 Extra 栏,其信息是 MySQL 对指定 SQL 执行计划的一些更加易读的数据,类似于提示信息。比如,是否用到临时表?是否用到排序?是否索引下推到引擎层?是否需要回表?等等。
接下来我们逐一讲解常见的 Extra 信息。
1Using index
这条提示信息是最为常见的,表示这条 SQL 可以不用回表,只需要扫描现存索引结构即可拿到所需记录;需要对索引中的记录按照索引顺序进行扫描;一般来讲,默认的索引顺序都是升序的,也就是从小到大的顺序进行扫描。
举个例子:
localhost:ytt>desc select r1 from t1 where1orderby r1\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: index
possible_keys: NULL
key: idx_r1
key_len: 5
ref: NULL
rows: 101745
filtered: 100.00
Extra: Usingindex
1rowinset, 1warning (0.00 sec)
这条 SQL 只从表 t1
里拿字段 r1
,并且字段 r1
上也有索引,Extra 栏里此时就会显示 Using index 。
所以单从数据库角度来讲,很多公司里定制的开发规范都强调在写 SQL 时,只需拿必要字段而不推荐写 SELECT *
也是很有道理的。
2Backward index scan
这条提示信息代表此条 SQL 可以走索引,不过没有按照索引顺序扫描,而是反向扫描;一般来讲,这个提示信息发生在需要倒序扫描索引记录的业务。
举个例子:
localhost:ytt>desc select r1 from t1 where1orderby r1 desc\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: index
possible_keys: NULL
key: idx_r1
key_len: 5
ref: NULL
rows: 101745
filtered: 100.00
Extra: Backward indexscan; Using index
1 row in set, 1warning (0.00 sec)
和第一条 SQL 很类似, 不同的是这条 SQL 获取字段 r1
的顺序刚好相反。如果单从执行效率角度来讲,升序和倒序的方式其实没有什么大的差别,仅仅是索引数据存储的方式不同。不过我个人还是建议严格按照顺序来设计对应的索引,比如针对这条 SQL 来讲,如果可以的话,需要单独创建一个倒序索引:
localhost:ytt>alter table t1 add key idx_r1_desc(r1 desc);
Query OK, 0 rows affected (1.68 sec)
Records: 0 Duplicates: 0 Warnings: 0
3Select tables optimized away
这条提示信息表示此条 SQL 在 MySQL 里是以最优化的方式来执行。可以这样理解:SQL 不需要实际执行即可拿到结果,也就是说 SQL 在被优化器交给执行器之前就可以拿到结果,不需要实际执行;更简单来讲,就是不需要实际查询实体表,而仅仅依赖 SQL 过滤条件中的常量等就可以提前拿到结果。比如以下两种场景:
返回结果最多只能有一行。 这一行必须从一个确定的结果集中获取。
举个例子:
localhost:ytt>desc select min(r2),max(r3) from t1 \G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 101745
filtered: 100.00
Extra: NULL
1rowinset, 1warning (0.00 sec)
对于以上这条 SQL ,检索表 t1
字段 r2
的最小值以及字段 r3
的最大值,需要全表扫,加个索引后,执行计划就变为如下:
localhost:ytt>alter table t1 addkey idx_r2(r2), addkey idx_r3(r3);
Query OK, 0 rows affected (2.86 sec)
Records: 0 Duplicates: 0 Warnings: 0
localhost:ytt>desc selectmin(r2),max(r3) from t1 \G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Selecttables optimized away
1rowinset, 1warning (0.00 sec)
此时,执行计划变为:Select tables optimized away
,也就是以最优的方式来得到结果,不需要访问实体表。
再来举个例子:
localhost:ytt>desc select min(r2) from t1 where r2 = 1\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Selecttables optimized away
1rowinset, 1warning (0.00 sec)
对于这条 SQL 来讲, 字段 r2=1
的结果集是固定的,并且 min(r2)
的结果刚好是 r2=1
, 从优化器角度来看,不需要访问实体表即可拿出结果。
4Zero limit
这个表示 SQL 语句使用了 LIMIT 0
子句。如果有需要检查语句合法性的需求,可以给语句后面加 LIMIT 0
,比如:
localhost:ytt>desc select r1 from t1 where1orderby r1 limit0\G
*************************** 1.row ***************************
id: 1
select_type: SIMPLE
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Zero limit
1rowinset, 1warning (0.00 sec)
当然不加 LIMIT 0
也是可以,MySQL 在语法分析阶段会自动给出 SQL 语句的合法性;加 LIMIT 0
主要是从业务的角度来体验 SQL 语句的合法性。
如果您认为这篇文章有些帮助,还请不吝点下文章末尾的"点赞"和"在看",或者直接转发朋友圈,





