项目场景:
今年工作变动,优化后在一家做国有项目的私人公司安顿下来了。公司环境不如以前,但是好在瑞欣依然可以每天方便的买到。人文氛围挺好,就是工时感觉有点紧,可能长期从事产品迭代开发,一下子转变做项目有点不能适应。另外,之前公司都取消了的福利这个新家还有,3大补贴、加班餐报销、零食管够,选择还多样,直接让行政点加班餐都行。
言归正传,今天遇到的是一个springboot服务搞CPU占用,持续在60%-%80,偶尔超过100%,这个服务都是数据采集工作,配合自动任务周期性执行。
问题描述
这个服务持续的高CPU占用,导致达梦数据库也高CPU占用,直接干到了700%多,且基本不难降下来。导致另一个下游业务的udpate执行超时,而且是根据id执行update都超时。
原因分析:
分析原因就是达梦数据库的资源被耗尽,没有多余资源响应。因为业务的问题,是循环里执行,可能每次update有几条不报错,但是中途有一条报错了就导致事务回滚了,最终是都没有成功。
原因分析过程有点坎坷,好多年没有直接面对运维方面的问题了。以前公司有运维顶着,很多时候都到不了研发这块。原因也基本运维就给定位了,到研发只是验证。
- 首先是觉得达梦没这么脆弱,所以就先去分析查看有没有sql阻塞、锁等。
-- 查询达梦数据库信息
SELECT * FROM V$VERSION;
-- 查询当前死锁信息
SELECT
lc.lmode,
lc.table_id,
lc.blocked,
vtw.id AS trx_id,
vs.sess_id,
vs.sql_text,
vs.appname,
vs.clnt_ip
FROM
v$lock lc
LEFT JOIN
v$trxwait vtw ON (lc.trx_id = vtw.id)
LEFT JOIN
v$trx vt ON (vtw.id = vt.id)
LEFT JOIN
v$sessions vs ON (vt.sess_id = vs.sess_id)
WHERE
vs.sql_text IS NOT NULL;
-- 查看涉及死锁的SESS_ID
SELECT
VTW.ID AS TRX_ID,
VS.SESS_ID,
VS.SQL_TEXT,
VS.APPNAME,
VS.CLNT_IP
FROM
V$TRXWAIT VTW
LEFT JOIN
V$TRX VT ON(VTW.ID = VT.ID)
LEFT JOIN
V$SESSIONS VS ON(VT.SESS_ID = VS.SESS_ID);
-- 查询正在执行的sql
select * from v$sessions where state = 'ACTIVE';
-- 解决死锁信息,参数为sess_id
-- sp_close_session(1111);
结果发现也就是执行时间长点,并没有死锁阻塞之类的。
-
然后,想着达梦咱也不熟悉(几乎没有用过,要说用就是上次来这家公司的二面-上机实现。以前公司大家一直都看不上达梦),所以就想着springboot高CPU长时间占用不正常,不如就先解决它把。
这次是把以前的jdk自带的分析套路先用上,见历史博文,jdk自带分析命令定位问题
然后,导出一份GC采样的日志,在收藏的工具网站上先分析一波。
这里让我惊讶的是服务器居然没有自己安装jdk,而是使用的自带的,还是仅仅是JRE,都不带JDK的一些分析工具,这是什么神操作啊。
确定暂时还没有死锁之类,也就是GC次数有点多。 -
最后,就想着收藏Arthas好久了,今天不妨就用下
安装之类的就不说了,直接去Arthas官方在线文档学习
想到既然用jdk自带的分析工具导出的GC采样日志分析有那么多的WAIT、TIMED_WAITING,那就直接看线程吧。
安装完成启动Arthas,选择springboot服务对应的pid,就可以开始表演了。有一点要说的就是,这个可以与项目同在,如果上服务器不方便,都可以直接把Arthas的终端暴露给外网。
java -jar arthas-boot.jar --target-ip 你能访问到的ip --http-port 8563 --telnet-port 3658
看效果
回归主题,上线程分析。
很快就定位到是业务代码的那一行了,业务代码就不给看了,自己玩去。
解决方案:
上面已经定位到业务代码了,自己去调整优化逻辑,是一个资深代码人应有的素养。
就写到这里,希望能帮到大家,uping!