分析一条sql的性能的标准总结
IT行业相对于一般传统行业,发展更新速度更快,一旦停止了学习,很快就会被行业所淘汰。所以我们需要踏踏实实的不断学习,精进自己的技术,尤其是初学者。今天golang学习网给大家整理了《分析一条sql的性能的标准总结》,聊聊sql性能,我们一起来看看吧!
这篇文章将给大家介绍如何使用 explain 来分析一条 sql 。
网上其实已经有非常多的文章都很详细的介绍了 explain 的使用,这篇文章将实例和原理结合起来,尽量让你有更好的理解,相信我,认真看完你应该会有特别的收获。
explain 翻译过来就是解释的意思, 在 mysql 里被称作执行计划,即可以通过该命令看出 mysql 在经过优化器分析后决定要如何执行该条 sql 。
说到优化器,再多说一句,mysql 内置了一个强大的优化器,优化器的主要任务就是把你写的 sql 再给优化一下,尽可能以更低成本去执行,比如扫描更少的行数,避免排序等。执行一条sql语句都经历了什么? 我在前面的文章中有介绍过优化器相关的。
你可能会问,一般在什么时候会要用 explain 呢,大多数情况下都是从 mysql 的慢查询日志中揪出来一些查询效率比较慢的 sql 来使用 explain 分析,也有的是就是在对 mysql 进行优化的时候,比如添加索引,通过 explain 来分析添加的索引能否被命中,还有的就是在业务开发的时候,在满足需求的情况下,你可能需要通过 explain 来选择一个更高效的 sql。
那么 explain 该怎么用呢,很简单,直接在 sql 前面加上 explain 就行了,如下所示。
mysql> explain select * from t; +----+-------------+-------+------+---------------+------+---------+------+--------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+--------+-------+ | 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 100332 | NULL | +----+-------------+-------+------+---------------+------+---------+------+--------+-------+ 1 row in set (0.04 sec)
可以看到,explain 会返回约 10 个字段,不同版本返回的字段有些许差异,每个字段都代表着具体的意义,这篇文章我不打算把每个字段都详细的介绍一遍,东西比较多,怕你也不容易记住,不如先把几个重要的字段好好理解了。
其中 type、key、rows、Extra 这几个字段我认为是比较重要的,我们接下来通过具体的实例来帮你更好的理解这几个字段的含义。
首先有必要简单介绍下这几个字段的字面意思。
type 表示 mysql 访问数据的方式,常见的有全表扫描(all)、遍历索引(index)、区间查询(range)、常量或等值查询(ref、eq_ref)、主键等值查询(const)、当表中只有一条记录时(system)。下面是效率从最好到最差的一个排序。
system > const > eq_ref > ref > range > index > all
key 表示查询过程实际会用到的索引名称。
rows 表示查询过程中可能需要扫描的行数,这个数据不一定准确,是mysql 抽样统计的一个数据。
Extra 表示一些额外的信息,通常会显示是否使用了索引,是否需要排序,是否会用到临时表等。
好了,接下来正式开始实例分析。
还是沿用前面文章中创建的存储引擎创建一个测试表,我们这里插入 10 w 条测试数据,表结构如下:
CREATE TABLE `t` ( `id` int(11) NOT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
然后看下面这条查询语句,注意这个表目前只有一个主键索引,还没有创建普通索引。
mysql> alter table t add index a_index(a); Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> alter table t add index b_index(b); Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> show index from t; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t | 0 | PRIMARY | 1 | id | A | 100332 | NULL | NULL | | BTREE | | | | t | 1 | a_index | 1 | a | A | 100332 | NULL | NULL | YES | BTREE | | | | t | 1 | b_index | 1 | b | A | 100332 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 3 rows in set (0.00 sec)
其中 type 值为 ALL,表示全表扫描了,大家注意看到 rows 这个字段显示有 100332 条,实际上我们一共才 10w 条数据,所以这个字段只是 mysql 的一个预估,并不一定准确。这种全表扫描的效率非常低,是需要重点被优化的。
接下来我们分别给字段 a 和 b 添加普通索引,然后再看下添加索引后的几条 sql 。
mysql> alter table t add index a_index(a); Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> alter table t add index b_index(b); Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> show index from t; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t | 0 | PRIMARY | 1 | id | A | 100332 | NULL | NULL | | BTREE | | | | t | 1 | a_index | 1 | a | A | 100332 | NULL | NULL | YES | BTREE | | | | t | 1 | b_index | 1 | b | A | 100332 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 3 rows in set (0.00 sec)
mysql> explain select * from t where a > 1000; +----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ | 1 | SIMPLE | t | ALL | a_index | NULL | NULL | NULL | 100332 | Using where | +----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ 1 row in set (0.00 sec)
上面这条 sql 看起来是不是有点疑惑呢,type 竟然显示刚刚不是给字段 a 添加索引了么,而且 possible_keys 也显示了有 a_index 可用,但是 key 显示 null,表示 mysql 实际上并不会使用 a 索引,这是为啥?
这里是因为 select * 的话还需要回到主键索引上查找 b 字段,这个过程叫回表,这条语句会筛选出 9w 条满足条件的数据,也就是说这 9w 条数据都需要回表操作,全表扫描都才 10w 条数据,所以在 mysql 的优化器看来还不如直接全表扫描得了,至少还免去了回表过程了。
当然也不是说只要有回表操作就不会命中索引,用不用索引关键还在于 mysql 认为哪种查询代价更低,我们把上面的 sql 中 where 条件再稍微改造一下。
mysql> explain select * from t where a > 99000; +----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+ | 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using index condition | +----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------+ 1 row in set (0.00 sec)
这回 type 值为 range 了,key 为 a_index ,表示命中了 a 索引,是一个不错的选择,是因为满足这条 sql 条件的只有 1000 条数据,mysql 认为 1000 条数据就算回表也要比全表扫描的代价低,所以说 mysql 其实是个很聪明的家伙。
我们还可以看到 Extra 字段中值为 Using index condition,这个意思是指用到了索引,但是需要回表,再看下面这个语句。
mysql> explain select a from t where a > 99000; +----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ | 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using where; Using index | +----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ 1 row in set (0.00 sec)
这个 Extra 中的值为 Using where; Using index ,表示查询用到了索引,且要查询的字段在索引中就能拿到,不需要回表,显然这种效率比上面的要高,所以不要轻易写 select * ,只查询业务需要的字段即可,这样可以尽可能避免回表。
再来看一个需要排序的。
mysql> explain select a from t where a > 99000 order by b; +----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+ | 1 | SIMPLE | t | range | a_index | a_index | 5 | NULL | 999 | Using index condition; Using filesort | +----+-------------+-------+-------+---------------+---------+---------+------+------+---------------------------------------+ 1 row in set (0.00 sec)
这个 Extra 中返回了一个 Using filesort,意味着需要排序,这种是需要重点优化的的,也就是说查到数据后,还需要 mysql 在内存中对其进行排序,你要知道索引本身就是有序的,所以一般来讲要尽量利用索引的有序性,比如像下面这样写。
mysql> explain select a from t where a > 99990 order by a; +----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+ | 1 | SIMPLE | t | range | a_index,ab_index | a_index | 5 | NULL | 10 | Using where; Using index | +----+-------------+-------+-------+------------------+---------+---------+------+------+--------------------------+ 1 row in set (0.00 sec)
我们再创建一个复合索引看看。
mysql> alter table t add index ab_index(a,b); Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from t where a > 1000; +----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+ | 1 | SIMPLE | t | range | a_index,ab_index | ab_index | 5 | NULL | 50166 | Using where; Using index | +----+-------------+-------+-------+------------------+----------+---------+------+-------+--------------------------+ 1 row in set (0.00 sec)
这条 sql 刚刚在上面也有讲到过,在没有创建复合索引的时候,是走的全表扫描,现在其实是利用了覆盖索引,同样是免去了回表过程,即在 (ab_index) 索引上就能找出要查询的字段。
这篇文章通过几个实例介绍了如何使用 explain 分析一条 sql 的执行计划,也提到了一些常见的索引优化,事实上还有更多的可能性,你也可以自己去写一个 sql ,然后使用 explain 分析,看看有哪些是可以被优化的。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。

- 上一篇
- navicat 8 for mysql建库的方法

- 下一篇
- MySQL启动报错问题InnoDB:Unable to lock/ibdata1 error
-
- 微笑的冬天
- 这篇文章真是及时雨啊,太细致了,真优秀,码起来,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-06-03 16:03:26
-
- 年轻的香氛
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢大佬分享技术文章!
- 2023-05-25 20:36:41
-
- 奋斗的龙猫
- 太全面了,已收藏,感谢作者大大的这篇文章,我会继续支持!
- 2023-04-23 00:11:59
-
- 光亮的滑板
- 这篇文章内容出现的刚刚好,很详细,很好,已收藏,关注博主了!希望博主能多写数据库相关的文章。
- 2023-04-18 20:57:06
-
- 大瓶可乐1989
- 这篇博文太及时了,作者大大加油!
- 2023-04-09 02:59:14
-
- 明亮的饼干
- 这篇技术贴真是及时雨啊,up主加油!
- 2023-04-06 19:01:11
-
- 文静的电话
- 这篇博文真及时,太全面了,写的不错,收藏了,关注博主了!希望博主能多写数据库相关的文章。
- 2023-03-06 14:58:52
-
- 干净的悟空
- 太详细了,码住,感谢师傅的这篇技术文章,我会继续支持!
- 2023-03-01 12:31:10
-
- 舒服的薯片
- 真优秀,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢老哥分享技术贴!
- 2023-02-17 01:03:02
-
- 谦让的月饼
- 这篇文章内容真及时,太详细了,写的不错,已收藏,关注作者了!希望作者能多写数据库相关的文章。
- 2023-02-01 03:51:45
-
- 朴实的棒球
- 太详细了,码起来,感谢楼主的这篇文章内容,我会继续支持!
- 2023-01-20 06:26:06
-
- 无奈的冷风
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢up主分享文章!
- 2023-01-12 07:12:30
-
- 数据库 · MySQL | 3小时前 | 数据类型 扩展性 存储引擎 CREATETABLE 约束条件
- MySQL建表攻略:详解数据表创建方法
- 265浏览 收藏
-
- 数据库 · MySQL | 6小时前 | mysql 字符集 中文乱码 utf8mb4 utf8mb4_unicode_ci
- MySQL中文乱码解决方案与字符集修改命令大全
- 339浏览 收藏
-
- 数据库 · MySQL | 1天前 | 索引 数据类型 字符集 存储引擎 CREATETABLE
- MySQL新建表操作指南与建表技巧
- 462浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 笔灵AI生成答辩PPT
- 探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
- 22次使用
-
- 知网AIGC检测服务系统
- 知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
- 32次使用
-
- AIGC检测-Aibiye
- AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
- 36次使用
-
- 易笔AI论文
- 易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
- 45次使用
-
- 笔启AI论文写作平台
- 笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
- 38次使用
-
- MySQL主从切换的超详细步骤
- 2023-01-01 501浏览
-
- Mysql-普通索引的 change buffer
- 2023-01-25 501浏览
-
- MySQL高级进阶sql语句总结大全
- 2022-12-31 501浏览
-
- Mysql报错:message from server: * is blocked because of many
- 2023-02-24 501浏览
-
- 腾讯云大佬亲码“redis深度笔记”,不讲一句废话,全是精华
- 2023-02-22 501浏览