记一次MySQL CPU被打满的SQL优化案例分析

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
EMR Serverless StarRocks,5000CU*H 48000GB*H
函数计算FC,每月免费额度15元,12个月
简介: 记一次MySQL CPU被打满的SQL优化案例分析

背景介绍

系统中有个公告模块,当用户登录后,根据用户所属机构查询公告列表,同时公告列表中需要展示出该用户对公告的阅读状态及阅读时间。公告(bulletin)、公告接收者(bulletin_receiver)、公告阅读者(bulletin_reader)定义及关联关系如下:

应用使用的数据库连接池是druid,数据库是阿里云RDS MySQL 5.6(16c64g)

问题梳理

经过梳理,事件时间线大概是这样的:

14:45 GetConnectionTimeoutException

14:47 钉钉报警

14:59 CommunicationsException

问题分析

应用分析

GetConnectionTimeoutException

arthas vmtool

我们分析问题,通常会碰到日志记录不够排查问题的情况,此时可以通过arthas相关命令进行分析。比如我们想看一下druid连接池统计信息。

CommunicationsException

出现该异常几个典型场景是:

  • 数据库连接空闲时间超过了MySQL服务器配置的wait_timeout
  • MySQL server versions like 5.6.25 and earlier or 5.7.5 and earlier,客户端连接属性useSSL默认是false;MySQL server versions like 5.6.25+ or 5.7.5+,客户端连接属性useSSL默认是true。默认useSSL=true的MySQL server版本,客户端连接属性还需要配置其他额外的连接属性,如果没有配置会抛出_CommunicationsException异常。_
  • 客户端发送请求后,服务端比较忙,一直没有回复客户端的请求导致超时。

从上面时间线看,当出现该异常的时候,MySQL CPU已经被打满,无法及时处理客户端请求,导致客户端请求超时。

系统负载

业务指标

  • 业务入口请求量比较平缓,没有波动
  • 系统GC情况正常… …

数据库分析

数据库侧没有打开performance_schema,主要排查操作:

  • show processlist:查看数据库会话信息
  • select * from information_schema.innodb_trx
  • 查询慢SQL:没有慢SQL

可疑SQL:

SELECT A.ID AS ID,
   A.BULLETIN_TYPE AS BULLETIN_TYPE,
   A.BULLETIN_CONTENT AS BULLETIN_CONTENT,
   A.STATUS_CODE AS REMARKS,
   B.READ_STATUS AS STATUS_CODE,
   B.READ_TIME AS GMT_MODIFIED
FROM(
    SELECT C.*
      FROM BULLETIN C
     WHERE C.ID IN(
                SELECT D.BULLETIN_ID
                  FROM BULLETIN_RECEIVER AS D
                 WHERE D.RECV_CODE IN('45346600', '45302600')
                 GROUP BY D.BULLETIN_ID)
 AND C.STATUS_CODE= 'RELEASE'
 AND C.BULLETIN_TYPE= 2
 AND C.VALID_DATE_END> '2022-11-16 00:00:00'
 AND C.VALID_DATE_BEGIN<= '2022-11-17 00:00:00') AS A
LEFT JOIN BULLETIN_READER AS B ON A.ID= B.BULLETIN_ID
 AND B.READER_ID= 1990234
WHERE B.IS_DELETED IS NULL OR B.IS_DELETED= '0'
LIMIT 0,10

explain 执行计划:

似乎没有啥问题,而且SQL执行的还可以。

一时没有好的办法快速解决,扩容了两台从库用来分担读的流量,从库上线后一切还算正常,于是恢复了部分数据,哪知过了两天CPU再次被打满… …

问题分析到这里,似乎已经不是资源的问题了… …

解决办法

SQL优化

话说SQL里尽量不要使用IN,可以使用EXISTS或JOIN替代,于是我们改了下SQL。

EXISTS

SELECT A.ID AS ID,
   A.BULLETIN_TYPE AS BULLETIN_TYPE,
   A.BULLETIN_CONTENT AS BULLETIN_CONTENT,
   A.STATUS_CODE AS REMARKS,
   B.READ_STATUS AS STATUS_CODE,
   B.READ_TIME AS GMT_MODIFIED
FROM(
    SELECT C.*
      FROM BULLETIN C
     WHERE EXISTS (
                SELECT 1
                  FROM BULLETIN_RECEIVER AS D
                 WHERE D.BULLETIN_ID=C.ID AND D.RECV_CODE IN('45346600', '45302600')
                 )
 AND C.STATUS_CODE= 'RELEASE'
 AND C.BULLETIN_TYPE= 2
 AND C.VALID_DATE_END> '2022-11-16 00:00:00'
 AND C.VALID_DATE_BEGIN<= '2022-11-17 00:00:00') AS A
LEFT JOIN BULLETIN_READER AS B ON A.ID= B.BULLETIN_ID
 AND B.READER_ID= 1990234
WHERE B.IS_DELETED IS NULL OR B.IS_DELETED= '0'
LIMIT 0,10

JOIN

除了将IN改为JOIN,同时将select中的公告内容字段去掉,因为多数场景下公告列表不需要内容字段。

SELECT A.ID AS ID,
   A.BULLETIN_TYPE AS BULLETIN_TYPE,
   A.STATUS_CODE AS REMARKS,
   B.READ_STATUS AS STATUS_CODE,
   B.READ_TIME AS GMT_MODIFIED
FROM(
    SELECT C.ID AS ID,C.BULLETIN_TYPE AS BULLETIN_TYPE,C.STATUS_CODE AS STATUS_CODE
      FROM BULLETIN C
      JOIN (
              SELECT D.BULLETIN_ID AS ID
                FROM POR_BULLETIN_RECEIVER AS D
               WHERE D.RECV_CODE IN('45346600', '45302600') 
               GROUP BY D.BULLETIN_ID
              ) AS E ON C.ID=E.ID
 AND C.STATUS_CODE= 'RELEASE'
 AND C.BULLETIN_TYPE= 2
 AND C.VALID_DATE_END> '2022-11-16 00:00:00'
 AND C.VALID_DATE_BEGIN<= '2022-11-17 00:00:00') AS A
LEFT JOIN BULLETIN_READER AS B ON A.ID= B.BULLETIN_ID
 AND B.READER_ID= 1990234
WHERE B.IS_DELETED IS NULL OR B.IS_DELETED= '0'
LIMIT 0,10

验证

无论执行计划怎么样,还是实际验证下比较放心。

结论

IN、EXISTS性能几乎无差别,每次请求平均耗时56ms,JOIN每次请求平均耗时1ms。

服务拆分

公告模块目前与核心模块耦合有点紧,当公告模块出现问题的时候会直接影响到核心模块,需要将公告模块进行拆分。

公告内容字段存储优化

方案一

将公告内容字段独立到一个表中

方案二

目前公告字段是存储在MySQL中,大小从60B 到 25KB不等,从上面分析看这个字段对系统的伤害非常大,可以考虑将该字段放到OSS存储,数据库中保存OSS路径的方式。

完善监控

当发现我们掌握的信息不足以分析问题的时候,说明需要完善现有的监控指标

数据清理

根据数据保存规定,将过期数据及时清理出去。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
5天前
|
SQL 存储 安全
数据库数据恢复—SQL Server数据库出现逻辑错误的数据恢复案例
SQL Server数据库数据恢复环境: 某品牌服务器存储中有两组raid5磁盘阵列。操作系统层面跑着SQL Server数据库,SQL Server数据库存放在D盘分区中。 SQL Server数据库故障: 存放SQL Server数据库的D盘分区容量不足,管理员在E盘中生成了一个.ndf的文件并且将数据库路径指向E盘继续使用。数据库继续运行一段时间后出现故障并报错,连接失效,SqlServer数据库无法附加查询。管理员多次尝试恢复数据库数据但是没有成功。
|
21天前
|
SQL Java 数据库连接
2万字实操案例之在Springboot框架下基于注解用Mybatis开发实现基础操作MySQL之预编译SQL主键返回增删改查
2万字实操案例之在Springboot框架下基于注解用Mybatis开发实现基础操作MySQL之预编译SQL主键返回增删改查
26 2
|
21天前
|
关系型数据库 MySQL 数据库
关系型数据库MySQL开发要点之多表设计案例详解代码实现
关系型数据库MySQL开发要点之多表设计案例详解代码实现
30 2
|
21天前
|
关系型数据库 MySQL 数据库
MySQL数据库开发之多表查询数据准备及案例实操
MySQL数据库开发之多表查询数据准备及案例实操
24 1
|
27天前
|
存储 缓存 监控
MySQL 8.0中查询缓存的废弃与原因分析
MySQL 8.0中查询缓存的废弃与原因分析
40 1
|
1月前
|
SQL 关系型数据库 MySQL
MySQL数据库基础练习系列8、成绩录入与分析系统
MySQL数据库基础练习系列8、成绩录入与分析系统
14 1
|
1月前
|
SQL 缓存 关系型数据库
✅分析SQL执行计划,我们需要关注哪些重要信息
SQL执行计划解析:12个关键字段详解,包括id(操作标识)、select_type(操作类型)、table(涉及表)、partitions(分区)、type(索引类型)、possible_keys(可能的索引)、key(使用索引)、key_len(索引长度)、ref(比较对象)、rows(扫描行数)、filtered(过滤比例)和Extra(额外信息)。类型从优至劣:system&gt;const&gt;eq_ref&gt;ref&gt;range&gt;index&gt;ALL。
|
1月前
|
SQL 关系型数据库 MySQL
基于Hive的天气情况大数据分析系统(通过hive进行大数据分析将分析的数据通过sqoop导入到mysql,通过Django基于mysql的数据做可视化)
基于Hive的天气情况大数据分析系统(通过hive进行大数据分析将分析的数据通过sqoop导入到mysql,通过Django基于mysql的数据做可视化)
|
13天前
|
SQL 存储 大数据
SQL中DISTINCT关键字的使用与性能影响分析
SQL中DISTINCT关键字的使用与性能影响分析
|
14天前
|
缓存 弹性计算 监控
云服务器 CPU 使用率高的问题排查与优化
云服务器 CPU 使用率高的问题排查与优化
26 0

热门文章

最新文章