MySQL8.0.18加强EXPLAIN,定位sql性能更直观

在mysql8.0.18版本之前,查看一个sql语句的执行计划,基本职能看到,sql是否走索引,使用哪个索引,是否是全部扫描,是否用到文件排序等等,如下所示

mysql> explain select a.name,a.depno,b.name from t_test1 a inner join t_test2 b on a.depno=b.depno;+----+-------------+-------+------------+-------+-------------------+------+---------+------+------+----------+----------------------------------------------------+| id | select_type | table | partitions | type  | possible_keys     | key  | key_len | ref  | rows | filtered | Extra                                              |+----+-------------+-------+------------+-------+-------------------+------+---------+------+------+----------+----------------------------------------------------+|  1 | SIMPLE      | b     | NULL       | index | PRIMARY           | name | 81      | NULL |    3 |   100.00 | Using index                                        ||  1 | SIMPLE      | a     | NULL       | ALL   | idx_t_test1_depno | NULL | NULL    | NULL |    6 |    33.33 | Using where; Using join buffer (Block Nested Loop) |+----+-------------+-------+------------+-------+-------------------+------+---------+------+------+----------+----------------------------------------------------+2 rows in set, 1 warning (0.01 sec)

这样用表格方式展示,不直观,不是很方便去定位sql语句的性能瓶颈点,而且没有sql在每一步需要的cost,和预测执行时间。

MySQL8.0.18加强EXPLAIN,定位sql性能更直观

经过mysql社区贡献,这些痛点都一一解决,现在新版本支持以tree方式展现,而且在关键步骤都有cost,可以非常方便,直观的看到sql哪一步有性能瓶颈,下面个一个示例

mysql> EXPLAIN FORMAT=tree select a.name,a.depno,b.name from t_test1 a inner join t_test2 b on a.depno=b.depno;+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| EXPLAIN                                                                                                                                                                      |+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| -> Inner hash join (a.depno = b.depno)  (cost=2.60 rows=6)    -> Table scan on a  (cost=0.15 rows=6)    -> Hash        -> Index scan on b using name  (cost=0.55 rows=3) |+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.03 sec)

那有没有更高级的功能呢,在每一个步骤展示预估执行时间,这个在MySQL8.0.18同样支持,在explain后面加上ANALYZE就可以

mysql> EXPLAIN ANALYZE select a.name,a.depno,b.name from t_test1 a inner join t_test2 b on a.depno=b.depno;+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| EXPLAIN                                                                                                                                                                                                                                                                                                    |+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| -> Inner hash join (a.depno = b.depno)  (cost=2.60 rows=6) (actual time=3.303..3.317 rows=6 loops=1)    -> Table scan on a  (cost=0.15 rows=6) (actual time=0.546..0.555 rows=6 loops=1)    -> Hash        -> Index scan on b using name  (cost=0.55 rows=3) (actual time=1.865..1.883 rows=3 loops=1) |+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+1 row in set (0.03 sec)

在这里观察仔细的小伙伴,会发现,actual time有两个时间,这两个时间,我解释一下
Actual time 获取第一行的消耗的时间 (in milliseconds)
Actual time 获取第所有行的消耗的时间 (in milliseconds)

可能有的小伙伴会问,actual time=1.865..1.883 rows=3 loops=1),为什么获取第一条记录要1.865,而获取3条记录不是5.595,而是1.883,这是因为,这3条记录都在一个page页,数据库最小读取单位是page页,一次IO就能把3条记录读取,后面的动作都是在内存里操作,基本不消耗时间。

在这里我再延伸讲解一下,数据库的二级索引,大家还是看上面的sql执行计划

select a.name,a.depno,b.name from t_test1 a inner join t_test2 b on a.depno=b.depnomysql> show create table t_test2\G;*************************** 1. row ***************************       Table: t_test2Create Table: CREATE TABLE `t_test2` (  `depno` int(11) NOT NULL,  `name` char(20) DEFAULT NULL,  PRIMARY KEY (`depno`),  KEY `name` (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.02 sec)

MySQL8.0.18加强EXPLAIN,定位sql性能更直观

不知道小伙伴们,发现没有,possible_keys为PRIMARY,但是key却是name,而不是depno。很奇怪吧,这里有2个原因
原因一:在select要查询的列是t_test2表的name列
原因二:join条件是主键列depno

在这里就不得不先说一下mysql的二级索引,在mysql数据中所有的数据都是存储在索引中,主键基本都是聚簇索引,存储是记录行和主键列,二级索引,存储的是索引列值和主键值。下面用图来说明

MySQL8.0.18加强EXPLAIN,定位sql性能更直观


MySQL8.0.18加强EXPLAIN,定位sql性能更直观

回到上面sql语句的执行计划,为什么用了name列的二级索引,这是因为二级索引里存储了name和depno两个值,引擎只需要扫描name索引,就可以获取name和depno,而不用再去访问主键的聚簇索引。

您可能还会对下面的文章感兴趣: