MySQL 8.0 OCP (1Z0-908) 考点精析-性能优化考点3:EXPLAIN ANALYZE

勤奋不是嘴上说说而已,而是实际的行动,在勤奋的苦度中持之以恒,永不退却。业精于勤,荒于嬉;行成于思,毁于随。在人生的仕途上,我们毫不迟疑地选择勤奋,她是几乎于世界上一切成就的催产婆。只要我们拥着勤奋去思考,拥着勤奋的手去耕耘,用抱勤奋的心去对待工作,浪迹红尘而坚韧不拔,那么,我们的生命就会绽放火花,让人生的时光更加的闪亮而精彩。

导读:本篇文章讲解 MySQL 8.0 OCP (1Z0-908) 考点精析-性能优化考点3:EXPLAIN ANALYZE,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

MySQL 8.0 OCP (1Z0-908) 考点精析-性能优化考点3:EXPLAIN ANALYZE

EXPLAIN ANALYZE介绍

EXPLAIN ANALYZE是MySQL 8.0.18版本以后引入的新功能,用于对EXPLAIN命令的扩展。
其输出结果类似EXPLAIN ( format=tree )一样输出查询计划和估计成本之外,还会输出执行计划中各个迭代器的实际成本和执行时间等信息,帮助用户分析和了解慢SQL的瓶颈。

-预估的执行成本
-预估的返回行数
-实际返回第一条的时间 (ms)
-实际返回所有行的时间 (ms)
-实际迭代器返回的行数 (rows) 
-实际循环次数  loops

EXPLAIN ANALYZE具体语法如下。

{EXPLAIN | DESCRIBE | DESC} ANALYZE [FORMAT = TREE] select_statement

虽然我们常常使用DESCRIBE获取表结构的信息,EXPLAIN用于获取查询执行计划,实际上EXPLAIN和DESCRIBE 语句其实是同义词也就是功能相同。

例:

mysql> desc t1;
+------------+-------------------+------+-----+---------+-------+
| Field      | Type              | Null | Key | Default | Extra |
+------------+-------------------+------+-----+---------+-------+
| actor_id   | smallint unsigned | NO   |     | 0       |       |
| first_name | varchar(45)       | NO   |     | NULL    |       |
| last_name  | varchar(45)       | NO   |     | NULL    |       |
| film_info  | text              | YES  |     | NULL    |       |
+------------+-------------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

mysql> explain t1;
+------------+-------------------+------+-----+---------+-------+
| Field      | Type              | Null | Key | Default | Extra |
+------------+-------------------+------+-----+---------+-------+
| actor_id   | smallint unsigned | NO   |     | 0       |       |
| first_name | varchar(45)       | NO   |     | NULL    |       |
| last_name  | varchar(45)       | NO   |     | NULL    |       |
| film_info  | text              | YES  |     | NULL    |       |
+------------+-------------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

EXPLAIN ANALYZE的特性

  1. EXPLAIN ANALYZE 不仅可以分析SELECT语句,也可以分析UPDATE和DELETE语句,MySQL 8.0.19开始还支持TABLE语句。
  2. 通过EXPLAIN ANALYZE分析SQL语句的时候,会执行SQL但是不返回结果,同时收集执行信息。

例:使用 EXPLAIN ANALYZEDELETE语句

mysql> select count(*) from t1;
+----------+
| count(*) |
+----------+
|      200 |
+----------+
1 row in set (0.02 sec)

mysql>  explain analyze delete from t1 where t1.actor_id in (select actor_id from actor_info);
+--------------------------------------------------------------------------------------------------------
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+--------------------------------------------------------------------------------------------------------
| -> Delete from t1 (immediate)  (cost=23642.12 rows=235996) (actual time=848.799..848.799 rows=0 loops=1)
    -> Nested loop inner join  (cost=23642.12 rows=235996) (actual time=848.443..848.790 rows=200 loops=1)
        -> Table scan on t1  (cost=22.50 rows=200) (actual time=0.042..0.273 rows=200 loops=1)
        -> Single-row index lookup on <subquery2> using <auto_distinct_key> (actor_id=t1.actor_id)  (cost=5240.57..5240.57 rows=1) (actual time=4.242..4.242 rows=1 loops=200)
            -> Materialize with deduplication  (cost=5240.57..5240.57 rows=1180) (actual time=848.395..848.395 rows=200 loops=1)
                -> Table scan on actor_info  (cost=5105.35..5122.57 rows=1180) (actual time=848.265..848.305 rows=200 loops=1)
                    -> Materialize  (cost=5105.33..5105.33 rows=1180) (actual time=848.262..848.262 rows=200 loops=1)
                        -> Group aggregate: group_concat(distinct concat(c.`name`,': ',(select #4)) order by c.`name` ASC separator '; ')  (cost=4987.33 rows=1180) (actual time=2.783..847.454 rows=200 loops=1)
                            -> Nested loop left join  (cost=4441.13 rows=5462) (actual time=0.415..39.124 rows=5462 loops=1)
                                -> Nested loop left join  (cost=2529.43 rows=5462) (actual time=0.406..28.275 rows=5462 loops=1)
                                    -> Nested loop left join  (cost=617.73 rows=5462) (actual time=0.393..7.094 rows=5462 loops=1)
                                        -> Sort: a.actor_id, a.first_name, a.last_name  (cost=20.25 rows=200) (actual time=0.374..0.460 rows=200 loops=1)
                                            -> Table scan on a  (cost=20.25 rows=200) (actual time=0.009..0.274 rows=200 loops=1)
                                        -> Covering index lookup on fa using PRIMARY (actor_id=a.actor_id)  (cost=0.27 rows=27) (actual time=0.003..0.031 rows=27 loops=200)
                                    -> Covering index lookup on fc using PRIMARY (film_id=fa.film_id)  (cost=0.25 rows=1) (actual time=0.003..0.004 rows=1 loops=5462)
                                -> Single-row index lookup on c using PRIMARY (category_id=fc.category_id)  (cost=0.25 rows=1) (actual time=0.002..0.002 rows=1 loops=5462)
                        -> Select #4 (subquery in projection; dependent)
                            -> Aggregate: group_concat(f.title order by f.title ASC separator ', ')  (cost=24.84 rows=1) (actual time=0.144..0.144 rows=1 loops=5462)
                                -> Nested loop inner join  (cost=22.10 rows=27) (actual time=0.046..0.142 rows=3 loops=5462)
                                    -> Nested loop inner join  (cost=12.55 rows=27) (actual time=0.005..0.074 rows=28 loops=5462)
                                        -> Covering index lookup on fa using PRIMARY (actor_id=a.actor_id)  (cost=2.99 rows=27) (actual time=0.002..0.028 rows=28 loops=5462)
                                        -> Single-row index lookup on f using PRIMARY (film_id=fa.film_id)  (cost=0.25 rows=1) (actual time=0.001..0.001 rows=1 loops=154076)
                                    -> Single-row covering index lookup on fc using PRIMARY (film_id=fa.film_id, category_id=c.category_id)  (cost=0.25 rows=1) (actual time=0.002..0.002 rows=0 loops=154076)
 |
+--------------------------------------------------------------------------------------------------------
1 row in set, 2 warnings (0.86 sec)

mysql>
mysql> select count(*) from t1;
+----------+
| count(*) |
+----------+
|      200 |
+----------+
1 row in set (0.02 sec)

通过上面的输出,我们可以看到delete的执行过程和具体执行信息,但是不会实际删除数据。

EXPLAIN 和EXPLAIN ANALYZE的结果对比

下面我们分别通过explain format=tree 和EXPLAIN ANALYZE对SQL语句进行分析。

mysql> explain format=tree select count(*) from actor_info where actor_id in (select actor_id from actor);
+----------------------------------------------------------------------------------------
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+----------------------------------------------------------------------------------------
| -> Aggregate: count(0)  (cost=48584.74 rows=1)
    -> Nested loop inner join  (cost=24985.12 rows=235996)
        -> Index scan on actor using idx_actor_last_name  (cost=20.25 rows=200)
        -> Index lookup on actor_info using <auto_key0> (actor_id=actor.actor_id)  (cost=5105.58..5112.17 rows=27)
            -> Materialize  (cost=5105.33..5105.33 rows=1180)
                -> Group aggregate: group_concat(distinct concat(c.`name`,': ',(select #4)) order by c.`name` ASC separator '; ')  (cost=4987.33 rows=1180)
                    -> Nested loop left join  (cost=4441.13 rows=5462)
                        -> Nested loop left join  (cost=2529.43 rows=5462)
                            -> Nested loop left join  (cost=617.73 rows=5462)
                                -> Sort: a.actor_id, a.first_name, a.last_name  (cost=20.25 rows=200)
                                    -> Table scan on a  (cost=20.25 rows=200)
                                -> Covering index lookup on fa using PRIMARY (actor_id=a.actor_id)  (cost=0.27 rows=27)
                            -> Covering index lookup on fc using PRIMARY (film_id=fa.film_id)  (cost=0.25 rows=1)
                        -> Single-row index lookup on c using PRIMARY (category_id=fc.category_id)  (cost=0.25 rows=1)
                -> Select #4 (subquery in projection; dependent)
                    -> Aggregate: group_concat(f.title order by f.title ASC separator ', ')  (cost=24.84 rows=1)
                        -> Nested loop inner join  (cost=22.10 rows=27)
                            -> Nested loop inner join  (cost=12.55 rows=27)
                                -> Covering index lookup on fa using PRIMARY (actor_id=a.actor_id)  (cost=2.99 rows=27)
                                -> Single-row index lookup on f using PRIMARY (film_id=fa.film_id)  (cost=0.25 rows=1)
                            -> Single-row covering index lookup on fc using PRIMARY (film_id=fa.film_id, category_id=c.category_id)  (cost=0.25 rows=1)
 |
+----------------------------------------------------------------------------------------
1 row in set, 2 warnings (0.00 sec)

mysql>
mysql> explain analyze select count(*) from actor_info where actor_id in (select actor_id from actor);
+----------------------------------------------------------------------------------------
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+----------------------------------------------------------------------------------------
| -> Aggregate: count(0)  (cost=48584.74 rows=1) (actual time=677.721..677.722 rows=1 loops=1)
    -> Nested loop inner join  (cost=24985.12 rows=235996) (actual time=677.413..677.696 rows=200 loops=1)
        -> Covering index scan on actor using idx_actor_last_name  (cost=20.25 rows=200) (actual time=0.051..0.093 rows=200 loops=1)
        -> Index lookup on actor_info using <auto_key0> (actor_id=actor.actor_id)  (cost=5105.58..5112.17 rows=27) (actual time=3.388..3.388 rows=1 loops=200)
            -> Materialize  (cost=5105.33..5105.33 rows=1180) (actual time=677.355..677.355 rows=200 loops=1)
                -> Group aggregate: group_concat(distinct concat(c.`name`,': ',(select #4)) order by c.`name` ASC separator '; ')  (cost=4987.33 rows=1180) (actual time=2.301..676.262 rows=200 loops=1)
                    -> Nested loop left join  (cost=4441.13 rows=5462) (actual time=0.236..31.592 rows=5462 loops=1)
                        -> Nested loop left join  (cost=2529.43 rows=5462) (actual time=0.227..22.852 rows=5462 loops=1)
                            -> Nested loop left join  (cost=617.73 rows=5462) (actual time=0.211..3.611 rows=5462 loops=1)
                                -> Sort: a.actor_id, a.first_name, a.last_name  (cost=20.25 rows=200) (actual time=0.186..0.297 rows=200 loops=1)
                                    -> Table scan on a  (cost=20.25 rows=200) (actual time=0.033..0.088 rows=200 loops=1)
                                -> Covering index lookup on fa using PRIMARY (actor_id=a.actor_id)  (cost=0.27 rows=27) (actual time=0.007..0.014 rows=27 loops=200)
                            -> Covering index lookup on fc using PRIMARY (film_id=fa.film_id)  (cost=0.25 rows=1) (actual time=0.003..0.003 rows=1 loops=5462)
                        -> Single-row index lookup on c using PRIMARY (category_id=fc.category_id)  (cost=0.25 rows=1) (actual time=0.001..0.001 rows=1 loops=5462)
                -> Select #4 (subquery in projection; dependent)
                    -> Aggregate: group_concat(f.title order by f.title ASC separator ', ')  (cost=24.84 rows=1) (actual time=0.114..0.114 rows=1 loops=5462)
                        -> Nested loop inner join  (cost=22.10 rows=27) (actual time=0.041..0.112 rows=3 loops=5462)
                            -> Nested loop inner join  (cost=12.55 rows=27) (actual time=0.009..0.053 rows=28 loops=5462)
                                -> Covering index lookup on fa using PRIMARY (actor_id=a.actor_id)  (cost=2.99 rows=27) (actual time=0.007..0.012 rows=28 loops=5462)
                                -> Single-row index lookup on f using PRIMARY (film_id=fa.film_id)  (cost=0.25 rows=1) (actual time=0.001..0.001 rows=1 loops=154076)
                            -> Single-row covering index lookup on fc using PRIMARY (film_id=fa.film_id, category_id=c.category_id)  (cost=0.25 rows=1) (actual time=0.002..0.002 rows=0 loops=154076)
 |
+----------------------------------------------------------------------------------------
1 row in set, 2 warnings (0.68 sec)

mysql>

在这里插入图片描述

我们可以看到explain analyze 和explain format=tree的结果的基础上多了(actual time=XXX…XXX rows=XXX loops=XXX)的内容。
也就是上面介绍的如下内容:

-actual time:实际返回第一条的时间 (ms)-
-实际返回所有行的时间 (ms)
-实际迭代器返回的行数 (rows) 
-loops:实际循环次数  loops

例题

Choose two. Examine this query and output:

mysql> EXPLAIN ANALYZE
               SELECT city.CountryCode, country.Name AS Country_Name , city.Name, city.District, city.Population
               FROM world.city
                           INNER JOIN world.country ON country.Code = city.CountryCode
               WHERE country.Continent = ' Asia ' AND city.Population > 1000000
               ORDER BY city.Population DESC\G

在这里插入代码片

Which two statements are true?
A) The country table is accessed as the first table, and then joined to the city table. 
B) 35 rows from the city table are included in the result. 
C) The optimizer estimates that 51 rows in the country table have Continent = ' Asia '. 
D) It takes more than 8 milliseconds to sort the rows. 
E) The query returns exactly 125 rows.

例题解析

  • A 正确:我们可以看到使用的是Nest loop方式进行结合(inner join),所以首先会访问驱动表(也就是第一个表)country,然后针对驱动表country的每条记录,利用country.Code = city.CountryCode这个条件,扫描一次内部表city表。
  • B 错误:每次通过索引扫描city时返回行数是rows =35,但是 通过Filter后返回行数是rows=2。
  • C 错误:优化器预估country表中满足条件(country.Continent = ’ Asia ‘)的记录为rows =34,实际行数为actual … rows=51。
  • D 错误:sort消耗时间是8.431 – 8.033
  • E 查询实际返回行数 actual … rows=125。
  • country表中满足条件的记录为rows=51,所以会对city循环执行51次 (loops=51)

参考答案:AE

参考

https://dev.mysql.com/doc/refman/8.0/en/explain.html
https://dev.mysql.com/doc/refman/8.0/en/explain.html#explain-analyze

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/141596.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!