CPU飙高的常见场景及处理办法

mysql进程飙高

简述

各位服务端开发的同学,在使用mysql的过程中,大概都有遇到过cpu突然飙高到的情况,比如达到了200%。数据库在执行查询或者修改数据时,系统需要消耗大量的cpu算力来维护存储系统和内存系统中数据的一致性。并发量大并且大量sql性能低的情况下,会导致cpu飙高,如果还开启了慢日志记录,会进一步导致性能恶化。

定位过程

  1. 使用top命令观察,确认是否由mysqlId导致
  2. 如果是mysqlId导致,show processlist,查看session情况,排查是否存在消耗资源的sql在执行
  3. 找出消耗高的sql,观察执行计划是否准确,索引是否缺失,或者实在是由于数据量过大造成。

处理办法

  1. 一般来说需要kill掉这些线程,同时观察cpu使用率是否下降
  2. 进行相应的调整(加缓存、改sql、限制连接数等)。
  3. 重新跑这些sql。
  4. 优化的过程,往往不是一步完成的,而是一步一步,执行一项优化措辞,再观察,再优化。

场景举例

某产品线上环境,cpu使用率达到900%+。首先从数据量、索引情况、缓存使用等方向入手,目测数据量不大,也就是百万级,接下来去定位索引、缓存问题。

  1. 经过排查,发现很多查询都是直接走mysql,没有用到缓存
  2. 既然没有用到缓存,则是大量请求全部查询MySQL导致。通过下面的命令查看:
show processlist;

发现类似很多相同的SQL语句,一直处于query状态中。

select id form user where user_name = 'xxxxx';

初步分析可能是 user_name 字段没有索引导致。接着查询user表的索引情况:

show index form user;

发现这个字段是没有建立索引。增加索引之后,该条SQL查询能够正常执行。
3. 没隔一会,又发生大量的请求超时问题。接着进行分析,发现是开启了 慢日志查询。大量的SQL查询语句超过慢日志设置的阀值,于是将慢日志关闭之后,速度瞬间提升。CPU的使用率基本保持在300%左右。但还不是理想状态。
4. 紧接着将部分实时查询数据的SQL语句,都通过缓存(redis)读写实现。观察一段时间后,基本维持在了70%~80%。

总结

  1. 不推荐在这种CPU使用过高的情况下进行慢日志的开启。因为大量的请求,如果真是慢日志问题会发生日志磁盘写入,性能很低。
  2. 直接通过show processlist命令查看,基本能清晰的定位出部分查询问题严重的SQL语句,在针对该SQL语句进行分析。一般可能就是索引、锁、查询大量字段、大表等问题导致。
  3. 再则一定要使用缓存系统,降低对MySQL的查询频次。
  4. 对于内存调优,也是一种解决方案。

java进程飙高

简述

java进程在不进行大量cpu运算的情况下,cpu应该在100~200%之间。但是一旦高并发场景,要么走到了死循环,要么做了大量的GC,容易导致cpu飙高至800~900%

定位过程

  1. 首先通过top命令查看当前占用cpu较高的进程pid
  2. 查看当前进程消耗cpu较高的线程pid,top -Hp pid
  3. 通过print命令将线程pid转为16进制,根据该16进制值去打印的堆栈日志中查询,查看该线程所驻留的方法位置。
  4. 通过jstack命令查看栈信息,定位到线程的具体代码
  5. 分析代码解决问题

处理办法

  1. 如果是空循环或者空自旋
    处理方式:可以使用Thread.sleep或者加锁,让线程适当阻塞。
  2. 在循环的代码逻辑中,创建大量的新对象导致频繁GC。比如,从mysql查出了大量的数据,比如100W以上等等。
    处理方式:可以减少对象的创建数量,或者,可以考虑使用 对象池。

场景举例

某产品线上环境,运行一段时间后发现对应的进程竟然占用了700%的CPU,导致服务器不堪重负,频繁宕机。

  1. 登录服务器,执行top命令,查看CPU占用情况,找到进程的pid
top

在这里插入图片描述
很容易发现,PID为29706的java进程的CPU飙升到700%多,且一直降不下来,很显然出现了问题。
2. 使用top -Hp命令查看该Java进程内所有线程的资源占用情况

top -Hp 29706

在这里插入图片描述
很容易发现,多个线程的CPU占用达到了90%多。我们挑选线程号为30309的线程继续分析。
3. 使用jstack命令定位代码

# printf “%x\n” 命令(tid指线程的id号)将以上10进制的线程号转换为16进制
printf "%x\n"  30309

在这里插入图片描述转换后的结果分别为7665,由于导出的线程快照中线程的nid是16进制的,而16进制以0x开头,所以对应的16进制的线程号nid为0x7665

# 通过使用dk自带命令jstack获取该java进程的线程快照并输入到文件中
jstack -l 29706 > ./jstack_result.txt

在jstack_result.txt 文件中根据线程好nid搜索对应的线程描述

cat jstack_result.txt |grep -A 100  7665

在这里插入图片描述
根据搜索结果,判断应该是ImageConverter.run()方法中的代码出现问题
当然,这里也可以直接采用一下命令来定位具体代码行

jstack 29706 |grep -A 200 7665
  1. 分析代码解决问题(规避空循环、频繁GC)
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值