MySQL 8.0新特性专栏目录

《MySQL开发规范》过时了,视图查询性能提升了一万倍
你真的会用EXPLAIN么,SQL性能优化王者晋级之路
索引三剑客之降序索引和不可见索引
千呼万唤始出来,MySQL 8.0索引三剑客之函数索引
双重密码,MySQL 8.0创新特性
sql_mode兼容性,MySQL 8.0 升级踩过的坑
警惕参数变化,MySQL 8.0 升级避免再次踩坑


你真的会用EXPLAIN么,SQL性能优化王者晋级之路

  • MySQL 8.0新特性专栏目录
  • 前言
  • 1. 青铜选手使用EXPLAIN - EXPLAIN
  • 2. 铂金选手使用EXPLAIN - EXPLAIN FOR CONNECTION
  • 3. 钻石选手使用EXPLAIN - EXPLAIN ANALYZE
  • 4. 最强王者使用EXPLAIN - OPTIMIZER_TRACE
  • 总结

前言

MySQL作为全球最流行的数据库,相关从业者不计其数,可以说十个码农里至少有九个使用过MySQL。MySQL的开发人员或者DBA,经常使用EXPLAIN语句来查看SQL的执行计划。EXPLAIN的解读文章多如牛毛,每个开发人员对EXPLAIN结果都有自己的理解。然而,你真的会使用EXPLAIN吗?

1. 青铜选手使用EXPLAIN - EXPLAIN

我们以sysbench的测试表sbtest1上的查询为例,来看看大家都是怎么使用EXPLAIN的。

# 测试表
Create Table: CREATE TABLE `sbtest1` (`id` int NOT NULL AUTO_INCREMENT,`k` int NOT NULL DEFAULT '0',`c` char(120) NOT NULL DEFAULT '',`pad` char(60) NOT NULL DEFAULT '',PRIMARY KEY (`id`),KEY `idx_k` (`k`) /*!80000 INVISIBLE */,KEY `idx_c`(`c`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

sbtest1上有两个二级索引idx_kidx_c,执行以下SQL:select pad from sbtest1 where c = ‘64613629’ and k = 64613629;

青铜选手一般直接使用explain select ...查看SQL的执行计划。

MySQL [sbtest]> explain select pad from sbtest1 where c = '64613629' and k = 64613629;
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key   | key_len | ref   | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | sbtest1 | NULL       | ref  | idx_c   | idx_c | 120     | const |    1 |     5.00 | Using where |
+----+-------------+---------+------------+------+---------------+-------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

根据EXPLAIN的输出结果,我们可以看到这条查询SQL的执行计划为二级索引idx_c的索引扫描。

那么实际SQL执行时,真的如EXPLAIN所显示的那样使用到了idx_c这个索引吗?


2. 铂金选手使用EXPLAIN - EXPLAIN FOR CONNECTION

针对上述测试表的例子,实际执行时到底是使用idx_k还是idx_c索引,其实我们是不知道的。

那么,要想知道SQL实际执行时的索引选择,也是有方法的。

这里,我们介绍一个EXPLAIN的高级用法 - explain for connectionexplain for connection可以查看正在执行的会话中SQL的执行计划。

还是以上面的测试表为例,首先,我们在会话1中执行SQL。

# Session 1
MySQL [sbtest]> select pad from sbtest1 where c = 64613629 and k = '64613629';

然后,开启另外一个会话2,在会话2中查看当前正在执行的SQL以及对应的PROCESSID。

# Session 2
MySQL [sbtest]> show processlist;
+----------+------------+----------------------+--------+------------------+---------+---------------------------------------------------------------+--------------------------------------------+
| Id       | User       | Host                 | db     | Command          | Time    | State                                                         | Info                                       |
+----------+------------+----------------------+--------+------------------+---------+---------------------------------------------------------------+--------------------------------------------+
| 20255132 | wang       | 127.0.0.1:57932      | sbtest | Query            |       0 | init                                                          | show processlist                           |
| 20264939 | wang       | 127.0.0.1:50656      | sbtest | Query            |       5 | executing                                                     | select pad from sbtest1 where c = 64613629 and k = '64613629' |
+----------+------------+----------------------+--------+------------------+---------+---------------------------------------------------------------+--------------------------------------------+

最后,根据show processlist显示的线程号,使用explain for connection {PROCESSID} 查看会话1的SQL执行计划。可以看到,会话1的执行计划实际上是个全表扫描,而非idx_c上的索引扫描。

# Session 2
MySQL [sbtest]> explain for connection 20264939;
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | sbtest1 | NULL       | ALL  | idx_c         | NULL | NULL    | NULL | 93776256 |   100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+

使用explain for connection可以查看指定会话的SQL执行计划。这样就可以看到当前正在实际执行的SQL的执行计划。有了这个神器,我们可以更加直接地验证SQL的执行计划。

然而,假设会话1的SQL执行持续时间很短,或者数据库中当前连接过多,想要找到指定SQL的连接并查看其执行计划,并不是一件易事。

要想更进一步,我们需要一个更加直观有效的查看执行计划的方法。


3. 钻石选手使用EXPLAIN - EXPLAIN ANALYZE

MySQL 8.0.18中引入了一种新形式的EXPLAIN语句 - EXPLAIN ANALYZEEXPLAIN ANALYZE可以将SQL的每一步执行计划的估算成本和实际成本以树形格式展示。 也就是说,EXPLAIN ANALYZE会先根据统计信息估算出SQL的执行成本,然后实际地执行SQL,再将SQL实际执行的成本和每一步的调用次数、返回行数等信息一并展示出来。我们可以对比估算和实际执行的情况,对SQL的执行计划有个全面的了解。

还是以上述测试表的查询为例,我们来验证一下EXPLAIN ANALYZE是否真的这么神奇。

# explain analyze查看实际执行计划
MySQL [sbtest]> explain analyze select pad from sbtest1 where c = 64613629 and k = '64613629';
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                           |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| -> Filter: ((sbtest1.c = 64613629) and (sbtest1.k = 64613629))  (cost=100959.75 rows=9938) (actual time=0.046..344.477 rows=1 loops=1)-> Table scan on sbtest1  (cost=100959.75 rows=993820) (actual time=0.041..243.610 rows=1000000 loops=1)|
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.35 sec)

可以看到,EXPLAIN ANALYZE的输出结果跟之前的EXPLAIN输出相比差别很大。在MySQL 8.0中,为了更清晰地展示执行计划,引入了format=tree的输出格式。(事实上,同时引入的还有另外一种format=json的输出格式,该格式对于程序中处理输出结果很有帮助)

树形输出格式的执行计划,阅读顺序秉持两个原则:

  1. 缩进越大的行越先执行;
  2. 缩进相同的行从上往下执行。

这个例子中,先执行缩进大的第二行:Table scan on sbtest1,即对sbtest1进行全表扫描;第一个括号中展示估算的执行成本为100959.75,估算返回行数为993820;第二个括号中展示实际的执行情况,actual time=0.041…243.610 表示获取第一行的执行时间是0.041ms,获取所有行的时间为243.610ms(注意,如果循环执行了多次,这里表示每次获取所有行的平均时间),实际返回行数100w,循环1次。

再执行缩进比较小的第一行:Filter,结果集过滤,第一个括号展示估算的执行成本为100959.75,估算返回行数为9938;第二个括号展示实际的执行情况,actual time=0.046…344.477表示获取第一行的执行时间是0.046ms,获取所有行的时间为344.477ms,实际返回行数1行,循环1次。(注意,缩进较小的行对应的返回所有行的时间是包括他之下的缩进较大的行的返回时间)。

EXPLAIN ANALYZE中最后一步显示这条SQL的实际执行时间为344.477ms,即总的执行时间约为0.35s(跟MySQL返回的SQL执行时间1 row in set (0.35 sec) 一致)。

那么,EXPLAIN ANALYZE展示的实际执行时间,是不是这条SQL的真实执行时间呢? 其实也并不是的。

我们来实际执行这条SQL,看看具体执行时间。

# 实际执行SQL,查看执行时间
MySQL [sbtest]> select pad from sbtest1 where c = 64613629 and k = '64613629';
+-------------------------------------------------------------+
| pad                                                         |
+-------------------------------------------------------------+
| 22195207048-70116052123-74140395089-76317954521-98694025897 |
+-------------------------------------------------------------+
1 row in set (0.29 sec)

可以看到,SQL实际执行时间为0.29s,时间短于EXPLAIN ANALYZE中显示的0.35s,相差了20%。是的,EXPLAIN ANALYZE追踪SQL的实际执行过程,自然会产生一些额外的开销。 EXPLAIN ANALYZE的执行开销还是有点大的,这里希望官方能够再优化一下。

不管怎么说,MySQL 8.0引入的EXPLAIN ANALYZE向我们清晰展示了SQL的估算执行成本和实际执行消耗,对于优化SQL性能有着非常大的价值。

没有使用过MySQL 8.0的EXPLAIN analyze 就不算真的会用EXPLAIN


4. 最强王者使用EXPLAIN - OPTIMIZER_TRACE

前面介绍了EXPLAIN的几种使用姿势,用好EXPLAIN工具我们就能够很好的应对SQL性能优化了。最后,我们介绍一个不用EXPLAIN也能查看优化器执行计划的方式,性能优化中的最强王者 - optimizer_trace

optimizer_trace可以将MySQL优化器的决策和执行过程统统记录下来,以JSON格式保存在INFORMATION_SCHEMA.OPTIMIZER_TRACE表,以便于DBA和开发人员深入了解优化器的决策全过程。

还是以上述测试SQL,我们来测试验证一下。

# 1.打开optimizer_trace
SET optimizer_trace="enabled=on";# 2. 执行测试SQL
select pad from sbtest1 where c = 64613629 and k = '64613629';# 查看INFORMATION_SCHEMA.OPTIMIZER_TRACE表
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;# 关闭optimizer_trace
SET optimizer_trace="enabled=off";

INFORMATION_SCHEMA.OPTIMIZER_TACER表中内容很长,截取其中的决策部分如下:

# optimizer_trace的部分输出
{"considered_execution_plans": [{"plan_prefix": [],"table": "`sbtest1`","best_access_path": {"considered_access_paths": [{"rows_to_scan": 993820,"access_type": "scan","resulting_rows": 993820,"cost": 100960,"chosen": true}]},"condition_filtering_pct": 100,"rows_for_plan": 993820,"cost_for_plan": 100960,"chosen": true

对于SQL执行计划与我们预料的结果不一致时,可以使用optimizer_trace这一终极大杀器。由于现实中在MySQL中执行的SQL并不复杂,所以一般情况下也就用不上这个核武器级别的工具。


总结

本文介绍了查看SQL优化器执行过程的几种方式:EXPLAINEXPLAIN FOR CONNECTIONEXPLAIN ANALYZEOPTIMIZER_TRACE,助力开发人员在SQL性能优化路上披荆斩棘,从青铜选手走向自强王者。

喜欢的同学麻烦点个关注和点赞

你真的会用EXPLAIN么,SQL性能优化王者晋级之路相关推荐

  1. SQL性能优化技巧,常见优化10经验,数据库查询好慢,还能怎么办

    我熟练应用ctrl c和ctrl v 开发curd代码好多年了. mysql查询为什么会慢,关于这个问题,在实际开发经常会遇到,而面试中,也是个高频题. 遇到这种问题,我们一般也会想到是因为索引. 那 ...

  2. SQL性能优化15个小技巧

    SQL性能优化15个小技巧 前言 sql优化是一个大家都比较关注的热门话题,无论你在面试,还是工作中,都很有可能会遇到. 如果某天你负责的某个线上接口,出现了性能问题,需要做优化.那么你首先想到的很有 ...

  3. SQL性能优化应该考虑哪些?

     1 .调整数据结构的设计.这一部分在开发信息系统之前完成,程序员需要考虑是否使用 ORACLE 数据库的分区功能,对于经常访问的数据库表是否需要建立索引等. 2 .调整应用程序结构设计.这一部分 ...

  4. Oracle SQL性能优化的40条军规

    Oracle SQL性能优化的40条军规 1. SQL语句执行步骤 语法分析> 语义分析> 视图转换 >表达式转换> 选择优化器 >选择连接方式 >选择连接顺序 & ...

  5. SQL性能优化常见措施(Lock wait timeout exceeded)

    SQL性能优化常见措施 目 录 1.mysql中explain命令使用 2.mysql中mysqldumpslow的使用 3.mysql中修改my.ini配置文件记录日志 4.mysql中如何加索引 ...

  6. ORACLE+SQL性能优化

      1. 访问Table的方式 ORACLE 采用两种访问表中记录的方式: a. 全表扫描 全表扫描就是顺序地访问表中每条记录. ORACLE采用一次读入多个数据块(database block)的方 ...

  7. MySQL高性能实战——part3——分析SQL,定位慢SQL(性能优化的前提)

    前言: 此文借鉴<MySQL高性能>一书,还有MySQL官方文档,笔者将通过自身的一些实战经验和阅读习惯对本书进行一个总结,整理,归纳出企业级开发中常用的优化案列和部分概念!! ​ 官方文 ...

  8. Sql性能优化看这一篇就够了

    前言: 一个优秀开发的必备技能:性能优化,包括:JVM调优.缓存.Sql性能优化等.本文主要讲基于Mysql的索引优化. 首先我们需要了解执行一条查询SQL时Mysql的处理过程: 其次我们需要知道, ...

  9. 高效sql性能优化极简教程

    一,sql性能优化基础方法论 对于功能,我们可能知道必须改进什么:但对于性能问题,有时我们可能无从下手.其实,任何计算机应用系统最终队可以归结为: cpu消耗 内存使用 对磁盘,网络或其他I/O设备的 ...

最新文章

  1. 2块钱就能买上千张人脸照片?央视曝光AI黑产,产业链太惊人了
  2. 腾讯/字节/华为/旷视 2022届实习面经—计算机视觉方向
  3. 自建ELK vs 日志服务(SLS)全方位对比
  4. gitlab使用方法
  5. 对传统视觉惯性的颠覆
  6. sqlite管理工具_Liquibase 数据库版本管理工具:1.安装
  7. java 重定向关键字_SpringMVC 转发、重定向
  8. html5网页设计大作业-dw企业网页设计带图片轮播留言 hbuilder大学生网页设计作业成品模板|百岁山矿泉水网页设计
  9. 关于射频同轴连接器的功率容量探讨
  10. 魔方cfop公式软件_【二阶篇】一个万能公式还原二阶魔方
  11. 前辈们的网络攻城狮心得
  12. 联想G40重装linux系统,联想g40-30如何重装系统_联想g40-30重装win7系统的图文教程-win7之家...
  13. 关于大成资源网这一个月大成网停更详细原因
  14. 事业单位采购计算机的申请报告,事业单位采购申请报告
  15. lmdb文件的读取和保存
  16. 嵌入式开发中的防御性C语言编程
  17. Python基础一(介绍)
  18. 组播路由协议基础——PIM-SM BSR工作机制
  19. CSDN 博客积分规则
  20. 条码软件如何实现扫描二维码显示汉字

热门文章

  1. c语言|||博客作业03
  2. set /a 和set /p
  3. c语言yzk头文件,创建进度栏的步骤
  4. KeyPress 和KeyDown 、KeyPress之间的区别
  5. CCIE-IPV6单播和组播地址
  6. visio画图保存去白边,不失真
  7. linux中文镜像文件iso下载地址,linux系统镜像iso文件下载
  8. java-net-php-python-jspm高校助学金评选管理系统设计PPT计算机毕业设计程序
  9. “双11”火热来袭,电商售后那些不得不说的事儿
  10. 最值得推荐的任务悬赏平台