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

本文涉及的产品
应用实时监控服务-用户体验监控,每月100OCU免费额度
函数计算FC,每月15万CU 3个月
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 记一次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
目录
相关文章
|
11天前
|
SQL 关系型数据库 MySQL
MySQL进阶突击系列(07) 她气鼓鼓递来一条SQL | 怎么看执行计划、SQL怎么优化?
在日常研发工作当中,系统性能优化,从大的方面来看主要涉及基础平台优化、业务系统性能优化、数据库优化。面对数据库优化,除了DBA在集群性能、服务器调优需要投入精力,我们研发需要负责业务SQL执行优化。当业务数据量达到一定规模后,SQL执行效率可能就会出现瓶颈,影响系统业务响应。掌握如何判断SQL执行慢、以及如何分析SQL执行计划、优化SQL的技能,在工作中解决SQL性能问题显得非常关键。
|
11天前
|
SQL 存储 关系型数据库
MySQL原理简介—1.SQL的执行流程
本文介绍了MySQL驱动、数据库连接池及SQL执行流程的关键组件和作用。主要内容包括:MySQL驱动用于建立Java系统与数据库的网络连接;数据库连接池提高多线程并发访问效率;MySQL中的连接池维护多个数据库连接并进行权限验证;网络连接由线程处理,监听请求并读取数据;SQL接口负责执行SQL语句;查询解析器将SQL语句解析为可执行逻辑;查询优化器选择最优查询路径;存储引擎接口负责实际的数据操作;执行器根据优化后的执行计划调用存储引擎接口完成SQL语句的执行。整个流程确保了高效、安全地处理SQL请求。
131 75
|
6天前
|
SQL 存储 关系型数据库
MySQL原理简介—10.SQL语句和执行计划
本文介绍了MySQL执行计划的相关概念及其优化方法。首先解释了什么是执行计划,它是SQL语句在查询时如何检索、筛选和排序数据的过程。接着详细描述了执行计划中常见的访问类型,如const、ref、range、index和all等,并分析了它们的性能特点。文中还探讨了多表关联查询的原理及优化策略,包括驱动表和被驱动表的选择。此外,文章讨论了全表扫描和索引的成本计算方法,以及MySQL如何通过成本估算选择最优执行计划。最后,介绍了explain命令的各个参数含义,帮助理解查询优化器的工作机制。通过这些内容,读者可以更好地理解和优化SQL查询性能。
|
2月前
|
SQL 存储 关系型数据库
【MySQL基础篇】全面学习总结SQL语法、DataGrip安装教程
本文详细介绍了MySQL中的SQL语法,包括数据定义(DDL)、数据操作(DML)、数据查询(DQL)和数据控制(DCL)四个主要部分。内容涵盖了创建、修改和删除数据库、表以及表字段的操作,以及通过图形化工具DataGrip进行数据库管理和查询。此外,还讲解了数据的增、删、改、查操作,以及查询语句的条件、聚合函数、分组、排序和分页等知识点。
【MySQL基础篇】全面学习总结SQL语法、DataGrip安装教程
|
2月前
|
SQL 关系型数据库 MySQL
MySQL 高级(进阶) SQL 语句
MySQL 提供了丰富的高级 SQL 语句功能,能够处理复杂的数据查询和管理需求。通过掌握窗口函数、子查询、联合查询、复杂连接操作和事务处理等高级技术,能够大幅提升数据库操作的效率和灵活性。在实际应用中,合理使用这些高级功能,可以更高效地管理和查询数据,满足多样化的业务需求。
305 3
|
24天前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决
|
10天前
|
关系型数据库 MySQL 数据库
Docker Compose V2 安装常用数据库MySQL+Mongo
以上内容涵盖了使用 Docker Compose 安装和管理 MySQL 和 MongoDB 的详细步骤,希望对您有所帮助。
82 42
|
1天前
|
关系型数据库 MySQL 网络安全
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
40 25
|
28天前
|
缓存 关系型数据库 MySQL
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
229 0
|
2月前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
73 3