写点什么

MySQL 慢查询(上):为啥会这么慢?

发布于: 2021 年 01 月 08 日
MySQL慢查询(上):为啥会这么慢?

发现的一些问题

问题 1

在过去的半年时间里,研发团队内部尝试抓了一波儿慢查询 SQL 跟进处理率。发现有些同学对于慢查询处理的思路就是看看有没有用到索引,没有用到就试图加一个,实在不行就甩锅给这种情况是历史设计问题或者自行判定为用户特殊操作下触发的小概率事件,随即便申请豁免掉...

这样其实问题没有根本上解决。


问题 2

还有就是网络上经常可以看到一些类似这样的文章:

“慢 SQL 性能优化大全”

“慢 SQL 性能优化看这篇就够了”

......  

其实内容大同小异,要么建议加索引,要么建议重写 SQL....

怎么说呢?知识点是对的,但不全面,这个很容易误导新同学,哈哈哈。

本文初衷

在业务项目发展过程中,我们常常会面对要处理 MySQL 慢查询问题,那我们应该如何分析解决问题呢?

部分同学在处理 MySQL 慢查询时候主要思路是加索引来解决,确实加索引是一个很好的解决问题的手段,但不是全部。既然慢查询作为问题,那就需要明确问题发生原因,和解决问题路径分析, 授人以鱼不如授人以渔,让我们一起来解锁 🔓 下 MySQL 处理慢查询的正确姿势。


本文计划主要让大家搞明白查询 SQL 为什么会变慢,废话不多说,直接开干~

写在前面

在业务项目发展过程中,我们常常会面对要处理 MySQL 慢查询问题,那我们应该如何分析解决问题呢?

部分同学在处理 MySQL 慢查询时候主要思路是加索引来解决,确实加索引是一个很好的解决问题的手段,但不是全部。既然慢查询是问题,那就需要明确问题发生原因,和解决问题路径分析。我们一起来 get 下 MySQL 慢查询的正确姿势。


本文主要内容包括:

1、查询 SQL 执行到底经历了什么?

2、查询 SQL 为什么会慢?

1. 查询 SQL 执行到底经历了什么?

首先需要明确:一个查询 SQL 的执行到底经历了什么?


数据库执行 SQL 的大致流程如下:

  • 建立与 MySQL 服务器连接(基础)

  • 客户端发送查询 SQL 到数据库,数据库验证是否有执行的权限

  • MySQL 服务器先检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果,否则继续流转;

  • MySQL 服务器语法解析器,进行词法与语法分析,预处理

  • 流转至查询优化器生成执行计划

  • 根据生成的执行计划,调用存储引擎暴露的 API 来执行查询

  • 将查询执行结果返回给客户端

  • 关闭 MySQL 连接


具体执行过程可能会因 MySQL 服务器具体配置和执行场景有一些差异。

1)如未开启应用查询缓存,则直接忽略查询缓存的检查;

2)执行过程中,如同时对于被扫描的行可能加锁,同时也可能会被其他 sql 阻塞

2. 查询 SQL 为什么会慢?

我们可以把查询 SQL 执行看做是一个任务的话,那它是由一些列子任务组成的,每个子任务都存在一定的时间消耗。通常情况下,导致慢查询最根本的问题就是需要访问的数据太多,导致查询不可避免的需要筛选大量的数据。


面对慢查询,我们需要注意以下两点:

1)查询了过多不需要的数据

2)扫描了额外的记录

2.1 查询了过多不需要的数据

MySQL 并不是只返回需要的数据,实际上会返回全部结果集再进行计算。

尤其是多表关联查询 select * 的情况,我们是不是真的需要全部的列呢?如果不是,那我们直接指定对应字段就好了。


例如我们要查询用户关联订单下的商品信息,如下所示:

SELECT *FROM users  LEFT JOIN orders ON orders.user_id = users.user_id  LEFT JOIN goods ON goods.good_id = orders.good_idWHERE users.name = 'zhangsan';
复制代码

这将返回三个表的全部数据列,可以调整为仅取需要的列:

SELECT goods.title, goods.descriptionFROM users  LEFT JOIN orders ON orders.user_id = users.user_id  LEFT JOIN goods ON goods.good_id = orders.good_idWHERE users.name = 'zhangsan';
复制代码

取出全部列,会让优化器无法完成索引覆盖扫描这类优化,还会为服务器带来额外的 I/O、内存和 CPU 的消耗。

2.2 扫描了额外的记录

此种情况大部分属于索引应用不当造成的(包括:应该建的索引没有建,或者未应用到最佳索引)。


示例表结构如下:

CREATE TABLE `test_table` (  `name` varchar(32) DEFAULT NULL,  `desc` varchar(32) DEFAULT NULL,  `age` int(16) DEFAULT NULL,  `id` bigint(11) DEFAULT NULL,  KEY `idx_age` (`age`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
复制代码

存在索引 `idx_age` 的情况下,查询执行计划结果展示如下:

EXPLAIN SELECT * FROM test_table WHERE age = 10;
复制代码



预估访问 1 行数据即可命中数据,如删除有效索引 `idx_age` 后则会变成全表扫描(ALL),预估需要扫描 121524 条记录才能完成这个查询,如下图所示:


总结

根据梳理 MySQL 中的 SQL 执行过程我们发现,任何流程的执行都存在其执行环境和规则,其实产生慢 SQL 的本质是:我们没有按照数据库的要求方式来执行 SQL

主要导致慢查询最根本的问题就是需要访问的数据太多,导致查询不可避免的需要筛选大量的数据。



限于文章篇幅,同时为了大家更好的阅读体验,后面会连续产出系列文章:

MySQL 慢查询(中)

    主要内容包括 如何定位慢查询问题和几种实用解决方案介绍

MySQL 慢查询(下)

    主要内容包括 高性能查询难题优化内容点总结

最后,欢迎大家持续关注~



系列推荐:

hMySQL慢查询(上):你知道为啥会慢么?

MySQL慢查询(中):正确的处理姿势,你get到了吗?

MySQL慢查询(下):问题解决,干货总结


发布于: 2021 年 01 月 08 日阅读数: 75
用户头像

坚持分享接地气儿的架构技术文章! 2018.02.26 加入

同名微信公众号「架构精进之路」,专注软件架构研究,技术学习与职业成长!坚持原创总结、沉淀和分享,希望能带给大家一些引导和启发,感谢各位的支持(关注、点赞、分享)!

评论

发布
暂无评论
MySQL慢查询(上):为啥会这么慢?