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

本文涉及的产品
可观测可视化 Grafana 版,10个用户账号 1个月
函数计算FC,每月15万CU 3个月
应用实时监控服务-应用监控,每月50GB免费额度
简介: 记一次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路径的方式。

完善监控

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

数据清理

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

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
18天前
|
SQL 存储 关系型数据库
【MySQL基础篇】全面学习总结SQL语法、DataGrip安装教程
本文详细介绍了MySQL中的SQL语法,包括数据定义(DDL)、数据操作(DML)、数据查询(DQL)和数据控制(DCL)四个主要部分。内容涵盖了创建、修改和删除数据库、表以及表字段的操作,以及通过图形化工具DataGrip进行数据库管理和查询。此外,还讲解了数据的增、删、改、查操作,以及查询语句的条件、聚合函数、分组、排序和分页等知识点。
【MySQL基础篇】全面学习总结SQL语法、DataGrip安装教程
|
19天前
|
存储 关系型数据库 MySQL
10个案例告诉你mysql不使用子查询的原因
大家好,我是V哥。上周与朋友讨论数据库子查询问题,深受启发。为此,我整理了10个案例,详细说明如何通过优化子查询提升MySQL性能。主要问题包括性能瓶颈、索引失效、查询优化器复杂度及数据传输开销等。解决方案涵盖使用EXISTS、JOIN、IN操作符、窗口函数、临时表及索引优化等。希望通过这些案例,帮助大家在实际开发中选择更高效的查询方式,提升系统性能。关注V哥,一起探讨技术,欢迎点赞支持!
112 5
|
1月前
|
关系型数据库 MySQL 数据库
数据库数据恢复—MYSQL数据库文件损坏的数据恢复案例
mysql数据库文件ibdata1、MYI、MYD损坏。 故障表现:1、数据库无法进行查询等操作;2、使用mysqlcheck和myisamchk无法修复数据库。
|
1月前
|
SQL 存储 缓存
MySQL进阶突击系列(02)一条更新SQL执行过程 | 讲透undoLog、redoLog、binLog日志三宝
本文详细介绍了MySQL中update SQL执行过程涉及的undoLog、redoLog和binLog三种日志的作用及其工作原理,包括它们如何确保数据的一致性和完整性,以及在事务提交过程中各自的角色。同时,文章还探讨了这些日志在故障恢复中的重要性,强调了合理配置相关参数对于提高系统稳定性的必要性。
|
1月前
|
SQL 关系型数据库 MySQL
MySQL 高级(进阶) SQL 语句
MySQL 提供了丰富的高级 SQL 语句功能,能够处理复杂的数据查询和管理需求。通过掌握窗口函数、子查询、联合查询、复杂连接操作和事务处理等高级技术,能够大幅提升数据库操作的效率和灵活性。在实际应用中,合理使用这些高级功能,可以更高效地管理和查询数据,满足多样化的业务需求。
156 3
|
1月前
|
SQL 关系型数据库 MySQL
MySQL导入.sql文件后数据库乱码问题
本文分析了导入.sql文件后数据库备注出现乱码的原因,包括字符集不匹配、备注内容编码问题及MySQL版本或配置问题,并提供了详细的解决步骤,如检查和统一字符集设置、修改客户端连接方式、检查MySQL配置等,确保导入过程顺利。
|
1月前
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
1月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
133 7
|
2月前
|
弹性计算 Kubernetes Perl
k8s 设置pod 的cpu 和内存
在 Kubernetes (k8s) 中,设置 Pod 的 CPU 和内存资源限制和请求是非常重要的,因为这有助于确保集群资源的合理分配和有效利用。你可以通过定义 Pod 的 `resources` 字段来设置这些限制。 以下是一个示例 YAML 文件,展示了如何为一个 Pod 设置 CPU 和内存资源请求(requests)和限制(limits): ```yaml apiVersion: v1 kind: Pod metadata: name: example-pod spec: containers: - name: example-container image:
314 1
|
2月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
886 2