记录线上数据库CPU飙升到60%的性能优化


theme: orange

一、背景

有一天,dba在数据库告警群找到我,说我们数据库CPU有规律性的尖刺,qps每次突然增加500+,尖刺时cpu飙升到60%,没尖刺时只有5%左右

image.png
image.png

这种情况对系统造成的稳定性风险极高,要我们尽快排查,尽早排除隐患。下面是当时的数据库qps监控

image.png
image.png

二、排查与沟通过程

由于是规律性的尖刺,我们想到我们的定时job, 我们业务有一个业务配置缓存数据,通过Java程序的定时job从数据库加载到本地内存的,而且时间也吻合。

通过查看代码,我们是一个单机的job每,5分钟加载一次,每台机器都是分页从数据库读取配置数据,每次读取100条,然后写到本地的内存里。

这里有两个问题,单机的job和分页查询,我们生产环境有50台机器,这样查询db的qps会放大,造成数据库压力扩大。

和dba进行了沟通,dba给了我们两条建议:

1、要我们不要分页查询,直接一次性查询所有的配置数据。

2、不要用本地缓存,直接使用redis,这样就一份数据,操作数据库的qps也降低了。

三、第一次优化

由于是c端系统,而且业务配置缓存是系统的热点数据,考虑到系统稳定性第一,我们第一次没有大的改动,试图调高了分页的limit大小,观测数据库的监控,cpu使用率有下降,但是还是有尖刺,这样还有慢sql情况。

image.png
image.png

四、第二次优化

由于尖刺仍然存在,对数据库还是有一定的压力,且现在的方案存在优化空间,为了彻底消除数据库隐患,因此我们开始了第二轮优化。

我们需要解决的问题:

1、降低数据库qps,消除cpu尖刺

2、不影响查询热点配置数据的性能

因为每台机器都请求数据库,分页查询请求,我们想着降低请求qps,因此我们去除原来这种单机定时加载缓存方式,换成加载缓存到redis,这样就只要一台机器启动一个定时任务了,这样可以降低数据库的qps。由一个定时任务每5分钟执行一次,加载到redis。

不影响原来的查询性能,肯定不能直接查询redis,因此我们引入了本地缓存框架Caffine,本地缓存从redis查询数据。这样就形成了二级缓存架构

image.png
image.png

整个缓存改造涉及三个阶段:

第一阶段:使用xxljob定时job加载缓存到redis

image.png
image.png

第二阶段:程序启动初次加载缓存,加载数据到本地缓存

image.png
image.png

第三阶段:Caffine缓存未命中场景,单线程从缓存或者数据库加载

image.png
image.png

五、测试与上线流程

这次属于技术升级,需要测试回归相关业务才能上线,整体测试与上线流程如下:

  • 1、测试回归业务功能,开关验证

  • 2、灰度验证

  • 3、分机器发布

  • 4、全量发布

先发布一台机器节点,观测了几天业务情况,观测没问题之后再分批次发布,直到所有机器节点发布完成。

六、最终效果

经过优化上线,数据库的qps和cpu使用率下来了,也没有了尖刺,彻底消除了数据库隐患。

image.png
image.png

七、总结

数据库是业务系统强依赖的中间件,保障其稳定性至关重要,本文是根据实际性能优化经验,从架构设计代码层面优化数据库的使用,降低数据库qps和cpu使用率,提高数据库的稳定性

通过这次优化实践,给以后业务功能的设计开发也有一定的启发,一个好的方案设计可以避免系统风险,提高资源利用率,作为程序员可以利用每次新功能的设计开发经验,不断的积累比较好的方案,提升我们自身的能力。

坚持相信有输入一定要有输出,关注我们一起学习沉淀技术,希望我们的技术能力越来越强。

image.png
image.png

本文由 mdnice 多平台发布

### 数据库线上环境常见问题及解决方案 数据库在生产环境中的运行稳定性至关重要,但在实际应用中,常常会遇到一些问题,如死锁、性能瓶颈、资源竞争等。以下是一些常见问题及其解决方案。 #### 1. 死锁问题 死锁是数据库并发处理中常见的问题,特别是在高并发的多线程环境下。其根本原因通常是资源竞争和锁的顺序不一致。例如,两个事务分别持有部分资源并等待对方释放其他资源,从而导致死锁[^3]。 **解决方案:** - **规范事务操作顺序**:确保所有事务按照相同的顺序访问资源,减少锁竞争的可能性。 - **采用适当的锁机制**:根据业务需求选择合适的锁级别(如行级锁、表级锁),避免不必要的锁粒度。 - **定期检查死锁日志**:在 MySQL 中,可以通过查看死锁日志(`SHOW ENGINE INNODB STATUS`)来分析死锁原因,并据此优化事务逻辑。 #### 2. CPU 使用率过高 在高并发场景下,CPU 使用率可能飙升至 100%,导致系统响应变慢甚至不可用。常见的原因包括死循环、复杂查询、频繁的 GC(垃圾回收)等[^5]。 **解决方案:** - **优化 SQL 查询**:使用索引、避免全表扫描、减少不必要的数据加载。 - **避免死循环**:在代码中严格控制循环条件,使用线程池限制并发线程数量。 - **性能监控与调优**:使用 `top`、`htop`、`vmstat` 等工具监控 CPU 使用情况,结合 `jstack`(Java 应用)或 `perf`(Linux)分析线程堆栈,定位热点代码。 #### 3. 数据库连接池耗尽 当数据库连接池配置不合理或存在连接泄漏时,可能会导致连接池耗尽,进而影响业务正常运行。 **解决方案:** - **合理配置连接池参数**:如最大连接数、超时时间、空闲连接回收策略。 - **使用连接池监控工具**:如 HikariCP、Druid 提供的监控面板,实时查看连接池状态。 - **代码层面优化**:确保每次数据库操作后及时释放连接,避免连接未关闭的情况。 #### 4. 慢查询与性能瓶颈 慢查询是数据库性能下降的主要原因之一,通常表现为响应时间变长、QPS(每秒查询数)下降等。 **解决方案:** - **启用慢查询日志**:在 MySQL 中启用 `slow_query_log`,记录执行时间超过阈值的 SQL。 - **使用 `EXPLAIN` 分析执行计划**:检查 SQL 是否使用了正确的索引,是否有全表扫描。 - **优化索引**:为高频查询字段添加合适的索引,避免重复索引、冗余索引。 #### 5. 数据库主从同步延迟 在主从架构中,主库写入频繁或网络波动可能导致从库同步延迟,影响数据一致性。 **解决方案:** - **优化主库写入压力**:通过分库分表、异步写入等方式减少主库负载。 - **调整同步参数**:如 `sync_binlog`、`innodb_flush_log_at_trx_commit` 等,平衡性能与一致性。 - **监控同步状态**:使用 `SHOW SLAVE STATUS` 检查 `Seconds_Behind_Master`,及时发现延迟问题。 #### 6. 磁盘空间不足 随着数据量的增长,磁盘空间可能逐渐耗尽,影响数据库的正常写入操作。 **解决方案:** - **定期清理历史数据**:设置数据保留策略,删除或归档过期数据。 - **扩展存储容量**:使用云数据库的自动扩容功能,或手动扩展物理磁盘。 - **压缩数据**:使用 InnoDB 表压缩、分区表等方式减少磁盘占用。 #### 7. 数据库安全与权限管理 权限配置不当可能导致数据泄露或误操作,影响系统安全性。 **解决方案:** - **最小权限原则**:为不同用户分配最小必要的权限,避免使用 `root` 用户进行日常操作。 - **定期审计权限**:使用 `SHOW GRANTS` 检查用户权限,及时清理不再使用的账户。 - **启用 SSL 加密连接**:防止数据在传输过程中被窃取。 --- ### 示例代码:使用 `EXPLAIN` 分析 SQL 执行计划 ```sql EXPLAIN SELECT * FROM orders WHERE user_id = 1001; ``` 该命令可以查看 SQL 查询是否使用了索引、扫描的行数等信息,帮助优化查询性能。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

服务端技术栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值