你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!
GitHub 20k Star 的Java工程师成神之路,不来了解一下吗!
GitHub 20k Star 的Java工程师成神之路,真的不来了解一下吗!
GitHub 20k Star 的Java工程师成神之路,真的真的不来了解一下吗!
前段时间我们新上了一个新的应用,因为流量一直不大,集群QPS大概只有5左右,写接口的rt在30ms左右。
因为最近接入了新的业务,业务方给出的数据是日常QPS可以达到2000,大促峰值QPS可能会达到1万。
所以,为了评估水位,我们进行了一次压测。压测在预发布环境执行。压测过程中发现,当单机QPS达到200左右时,接口的rt没有明显变化,但是CPU利用率急剧升高,直到被打满。

压测停止后,CPU利用率立刻降了下来。
于是开始排查是什么导致了CPU的飙高。
问题排查与解决
在压测期间,登录到机器,开始排查问题。
本案例的排查过程使用的阿里开源的Arthas工具进行的,不使用arthas,使用JDK自带的命令也是可以。
在开始排查之前,可以先看一下CPU的使用情况,最简单的就是使用top命令直接查看:
top - 10:32:38 up 11 days, 17:56, 0 users, load average: 0.84, 0.33, 0.18
Tasks: 23 total, 1 running, 21 sleeping, 0 stopped, 1 zombie
%Cpu(s): 95.5 us, 2.2 sy, 0.0 ni, 76.3 id, 0.0 wa, 0.0 hi, 0.0 si, 6.1 st
KiB Mem : 8388608 total, 4378768 free, 3605932 used, 403908 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 4378768 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3480 admin 20 0 7565624 2.9g 8976 S 241.2 35.8 649:07.23 java
1502 root 20 0 401768 40228 9084 S 1.0 0.5 39:21.65 ilogtail
181964 root 20 0 3756408 104392 8464 S 0.7 1.2 0:39.38 java
496 root 20 0 2344224 14108 4396 S 0.3 0.2 52:22.25 staragentd
1400 admin 20 0 2176952 229156 5940 S 0.3 2.7 31:13.13 java
235514 root 39 19 2204632 15704 6844 S 0.3 0.2 55:34.43 argusagent
236226 root 20 0 55836 9304 6888 S 0.3 0.1 12:01.91 systemd-journ
可以看到,进程ID为3480的Java进程占用的CPU比较高,基本可以断定是应用代码执行过程中消耗了大量CPU,接下来开始排查具体是哪个线程,哪段代码比较耗CPU。
首先,下载Arthas命令:
curl -L http://start.alibaba-inc.com/install.sh | sh
启动
./as.sh
使用Arthas命令"thread -n 3 -i 1000"查看当前"最忙"(耗CPU)的三个线程:

通过上面的堆栈信息,可以看出,占用CPU资源的线程主要是卡在JDBC底层的TCP套接字读取上。连续执行了很多次,发现很多线程都是卡在这个地方。
通过分析调用链,发现这个地方是我代码中有数据库的insert,并且使用TDDL来创建sequence,在sequence的创建过程中需要和数据库有交互。
但是,基于对TDDL的了解,TDDL每次从数据库中查询sequence序列的时候,默认会取出1000条,缓存在本地,只有用完之后才会再从数据库获取下一个1000条序列。
按理说我们的压测QPS只有300左右,不应该这么频繁的何数据库交互才对。但是,经过多次使用arthas的查看,发现大部分CPU都耗尽在这里。
于是开始排查代码问题。最终发现了一个很傻的问题,那就是我们的sequence创建和使用有问题:
public Long insert(T dataObject) {
if (dataObject.getId() == null) {
Long id = next();
dataObject.setId(id);
}
if (sqlSession.insert(getNamespace() + ".insert", dataObject) > 0) {
return dataObject.getId();
} else {
return null;
}
}
public Sequence sequence() {
return SequenceBuilder.create()
.name(getTableName())
.sequenceDao(sequenceDao)
.build();
}
/**
* 获取下一个主键ID
*
* @return
*/
protected Long next() {
try {
return sequence().nextValue();
} catch (SequenceException e) {
throw new RuntimeException(e);
}
}
是因为,我们每次insert语句都重新build了一个新的sequence,这就导致本地缓存就被丢掉了,所以每次都会去数据库中重新拉取1000条,但是只是用了一条,下一次就又重新取了1000条,周而复始。
于是,调整了代码,把Sequence实例的生成改为在应用启动时初始化一次。这样后面在获取sequence的时候,不会每次都和数据库交互,而是先查本地缓存,本地缓存的耗尽了才会再和数据库交互,获取新的sequence。
public abstract class BaseMybatisDAO implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
sequence = SequenceBuilder.create().name(getTableName()).sequenceDao(sequenceDao).build();
}
}
通过实现InitializingBean,并且重写afterPropertiesSet()方法,在这个方法中进行Sequence的初始化。
改完以上代码,提交进行验证。通过监控数据可以看出优化后,数据库的读RT有明显下降:

sequence的写操作QPS也有明显下降:

于是我们开始了新的一轮压测,但是发现,CPU的使用率还是很高,压测的QPS还是上不去,于是重新使用Arthas查看线程的情况。

发现了一个新的比较耗费CPU的线程的堆栈,这里面主要是因为我们用到了一个联调工具,该工具预发布默认开启了TDDL的采集(官方文档中描述为预发布默认不开启TDDL采集,但是实际上会采集)。
这个工具在打印日志过程中会进行脱敏,脱敏框架会调用Google的re2j进行正则表达式的匹配。
因为我的操作中TDDL操作比较多,默认采集大量TDDL日志并且进行脱敏处理,确实比较耗费CPU。
所以,通过在预发布中关闭DP对TDDL的采集,即可解决该问题。
总结与思考
本文总结了一次线上CPU飙高的问题排查过程,其实问题都不难,并且还挺傻的,但是这个排查过程是值得大家学习的。
其实在之前自己排查过很多次CPU飙高的问题,这次也是按照老方法进行排查,但是刚开始并没有发现太大的问题,只是以为是流量升高导致数据库操作变多的正常现象。
期间又多方查证(通过arthas查看sequence的获取内容、通过数据库查看最近插入的数据的主键ID等)才发现是TDDL的Sequence的初始化机制有问题。
在解决了这个问题之后,以为彻底解决问题,结果又遇到了DP采集TDDL日志导致CPU飙高,最终再次解决后有了明显提升。
所以,事出反常必有妖,排查问题就是一个抽丝剥茧的过程。
关于作者:Hollis,一个对Coding有着独特追求的人,阿里巴巴技术专家,《程序员的三门课》联合作者,《Java工程师成神之路》系列文章作者。
如果您有任何意见、建议,或者想与作者交流,都可以关注公众号【Hollis】,直接后台给我留言。
你要偷偷学会排查线上CPU飙高的问题,然后惊艳所有人!的更多相关文章
- 线上CPU飚高(死循环,死锁……)?帮你迅速定位代码位置
top基本使用: top命令参考本篇文章 查看内存和CPU的top命令,别看输出一大堆,理解了其实很简单 top 命令运行图: 第一行:基本信息 第二行:任务信息 第三行:CPU使用情况 第四行:物理 ...
- 线上CPU飚高(死循环,死锁...)
之前排除服务器内存暴增的问题,在此看到一篇类似的文章,做个类似的记录. 1.top基本使用 top 命令运行图: 第一行:基本信息 第二行:任务信息 第三行:CPU使用情况 第四行:物理内存使用情况 ...
- JVM 线上故障排查基本操作--CPU飙高
JVM 线上故障排查基本操作 CPU 飚高 线上 CPU 飚高问题大家应该都遇到过,那么如何定位问题呢? 思路:首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程.然后 ...
- 一次线上CPU高的问题排查实践
一次线上CPU高的问题排查实践 前言 近期某一天上班一开电脑,就收到了运维警报,有两台服务CPU负载很高,同时收到一线同事反馈 系统访问速度非常慢,几乎无响应. 一个美好的早晨,最怕什么就来什么.只好 ...
- 记一次linux通过jstack定位CPU使用过高问题或排查线上死锁问题
一.java定位进程 在服务器中终端输入命令:top 可以看到进程ID,为5421的cpu这列100多了. 记下这个数字:5421 二.定位问题进程对应的线程 然后在服务器中终端输入命令:top -H ...
- 线上CPU飙升100%问题排查,一篇足矣
一.引子 对于互联网公司,线上CPU飙升的问题很常见(例如某个活动开始,流量突然飙升时),按照本文的步骤排查,基本1分钟即可搞定!特此整理排查方法一篇,供大家参考讨论提高. 二.问题复现 线上系统突然 ...
- 线上CPU飙升100%问题排查
本文转载自线上CPU飙升100%问题排查 引子 对于互联网公司,线上CPU飙升的问题很常见(例如某个活动开始,流量突然飙升时),按照本文的步骤排查,基本1分钟即可搞定!特此整理排查方法一篇,供大家参考 ...
- Linux命令排查线上问题常用的几个
排查线上问题常用的几个Linux命令 https://www.cnblogs.com/cjsblog/p/9562380.html top 相当于Windows任务管理器 可以看到,输出结果分两部分, ...
- 生产系统CPU飙高问题排查
现状 生产系统CPU占用过高,并且进行了报警 排查方法 执行top命令,查看是那个进程导致的,可以确定是pid为22168的java应用导致的 执行top -Hp命令,查看这个进程的那个线程导致cpu ...
随机推荐
- How to using PyPI publish a Python package
How to using PyPI publish a Python package PyPI & Python package https://pypi.org/ main make a f ...
- AI & HR
AI & HR 数字化人才画像 人力资本数字化管理 通过"AI+数据+专家"的手段,将"数字化人才画像"作为服务起点与信息入口,扩展提供全场景的数字化人 ...
- ESLint error level
ESLint error level https://eslint.org/docs/user-guide/getting-started#configuration .eslintrc { &quo ...
- nodejs stream 创建读写流
const fs = require("fs"); const { Writable, Readable, Duplex, Transform } = require(" ...
- 一款基于 Web 的通用数据管理工具(转载)
一款基于 WEB 的通用数据管控工具 - CloudQuery 前言 前段时间,公司因为业务发展,数据量攀升,老板迫切需要一个工具对数据进行精细化管理,一是确实需要精细化管理:二是因为我们公司小,数据 ...
- 5分钟入门websocket
5 个步骤快速掌握消息发送和接收 获取您的 appkey 先注册一个irealtime账号,然后登录到后台管理端,创建一个免费应用,就能得到您的 appkey.点击注册 各种前端生态端集成 ireal ...
- HashMap什么时候进行扩容?
Threshold:table数组元素个数size的大小超过threshold且且Node<K,V>[] table数组长度没有超过64时时table数组扩容.当hashmap中的元素个数 ...
- Hexo一键部署到阿里云OSS并设置浏览器缓存
自建博客地址:https://bytelife.net,欢迎访问! 本文为博客自动同步文章,为了更好的阅读体验,建议您移步至我的博客 本文作者: Jeffrey 本文链接: https://bytel ...
- 原生JS快速实现拖放
原生 JS 快速实现拖放 拖放是很常见的一种交互效果,很多时候我们都会借助于第三方的控件来实现,其实用原生 js 实现起来也非常的方便.接下来我们就用原生 js 和 css 快速实现拖放效果 html ...
- 看完我的笔记不懂也会懂----MongoDB
MongoDb数据库学习 - 数据库的分类 - 数据库基本概念 - MongoDB常用指令 - MongoDB的CURD - sort({key:*[1,-1]}).limit(num).skip(n ...