「性能指标」CPU飙高排查实战

news2025/1/11 18:30:32

上篇提到了什么是 CPU 以及如何计算的。接下来主要说说实际工作中遇到 CPU 飙高如何去排查呢?

往期回顾:

聊聊性能指标 CPU 利用率如何计算的?

问题发现

前段时间我们新上线了一个应用,由于流量一直不大,集群的每秒查询率(QPS)大约只有 5。接口的响应时间大约在 30 毫秒左右。

最近我们接入了新的业务,业务方提供的数据显示,日常的 QPS 预计可以达到 2000,而在大促期间峰值 QPS 可能会达到 1 万。

为了评估系统的性能水平,我们进行了一次压力测试。测试在预发布环境进行。在压力测试过程中,我们观察到当单台服务器的 QPS 达到约 200 时,接口的响应时间没有明显变化,但是 CPU 利用率迅速上升,直到达到极限。

image.png

压力测试结束后,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 资源。接下来,我们需要进一步排查是哪个线程、哪段代码导致了这种情况。

首先,我们需要下载 Arthas 工具:

curl -L https://arthas.aliyun.com/install.sh | sh

启动:

./as.sh

使用 Arthas 命令 "thread -n 3 -i 1000"查看当前"最忙"(耗 CPU)的三个线程:

image.png

通过上述堆栈信息可以看出,占用 CPU 资源的线程主要是在 JDBC 底层的 TCP 套接字读取上阻塞。经过连续执行多次分析,发现很多线程都在这个地方卡住。

进一步分析调用链后发现,这个问题源自于我代码中的数据库 insert 操作,其中使用了 TDDL 来创建 sequence。在 sequence 的创建过程中,需要与数据库进行交互。

根据对 TDDL 的了解,它每次从数据库查询 sequence 序列时,默认会获取 1000 条,并在本地进行缓存,直到使用完这 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 语句时都重新构建了一个新的 sequence 对象,这导致本地缓存被清空。因此,每次都需要从数据库重新获取 1000 条 sequence,但实际上只使用了一条,下次又会重复这个过程。

为了解决这个问题,我们调整了代码,在应用启动时初始化了一个 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)明显下降。

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

image.png

于是我们开始了新的一轮压测,但是发现 CPU 的使用率仍然很高,压测的 QPS 还是无法达到预期。因此,我们决定重新使用 Arthas 工具查看线程的情况。

image.png

发现了一个 CPU 消耗较高的线程堆栈,主要是因为我们在使用一个联调工具时,该工具预发布状态下默认开启了 TDDL 的日志采集(尽管官方文档中描述预发布状态下默认不会开启 TDDL 采集,但实际上确实会进行采集)。

在该工具打印日志时,会进行数据脱敏操作,脱敏框架使用了 Google 的 re2j 进行正则表达式匹配。

由于我的操作涉及到大量的 TDDL 操作,而默认情况下会采集大量 TDDL 日志并进行脱敏处理,这导致了较高的 CPU 消耗。

因此,通过在预发布环境中关闭对 TDDL 的日志采集,可以有效解决这个问题。

总结

这篇总结回顾了一次线上 CPU 飙高问题的排查过程,虽然问题最终解决起来并不复杂,但排查过程中却有其独特的教育意义。

之前经验丰富的我按照惯例进行了排查,初始阶段并未发现明显问题,错误地将数据库操作增加归因于流量上升所致的正常情况。

通过多方查证(例如使用 arthas 查看序列获取情况,以及通过数据库查询最新插入数据的主键 ID 等方法),最终确认问题出在 TDDL 的序列初始化机制上。

解决了这个问题后,本以为问题彻底解决,却又遭遇到 DP 采集 TDDL 日志导致 CPU 飙高的情况,最终再次解决问题后系统性能有了显著提升。

因此,这个经历再次验证了“事出反常必有妖”,排查问题确实需要有耐心和系统性。

如有问题,欢迎微信搜索【码上遇见你】。

好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1873289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python25 Numpy基础

1.什么是Numpy NumPy(Numerical Python 的简称)是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。NumPy 的前身是 Numeric,这是一个由 Jim Hugunin 等人开发的…

Flexsim物流仿真入门-利用网络节点控制行走路线

在布局优化过程中,往往叉车,操作人员的搬运,是会按照一定的行走路线进行的,这就需要我们进行节点的设计,以此来实现特定路径的行走。 在构建过程中,这里将会以案例的形式进行讲解说明。 1)构建…

基于单片机的智能温控风扇设计

摘 要 : 本次设计是基于单片机的智能温控风扇 。 以 STC89C52 单片机为核心 , 可以实现对风扇的有效控制 。 可以根据需要设置不同的温度 ,如果温度在设定值最大值和最小值之间时则启动风扇弱风档, 如果温度超过设定的数值时将会变到大风档…

mfc140.dll怎么安装?mfc140.dll丢失安装详细解决方法

当电脑出现找不到mfc140.dll丢失问题,我们需要怎么办?怎么解决mfc140.dll丢失问题?mfc140.dll到底是什么?下面我给大家详细介绍与分析,最重要的是mfc140.dll的解决方法! 一、文件丢失原因分析 在分析mfc14…

C++笔记:实现一个字符串类(构造函数、拷贝构造函数、拷贝赋值函数)

实现一个字符串类String&#xff0c;为其提供可接受C风格字符串的构造函数、析构函数、拷贝构造函数和拷贝赋值函数。 声明依赖文件 其中ostream库用于打印标准输入输出&#xff0c;cstring库为C风格的字符串库 #include <iostream> #include <cstring> 声明命…

印度股市连续三天创新高,国际知名评级机构穆迪发出警告

KlipC报道&#xff1a;6月27日&#xff0c;印度股市连续三个交易日创新高&#xff0c;截至当日收盘&#xff0c;印度SENSEX30指数涨0.72%&#xff0c;报79243.18点。印度SENSEX30指数今年以来涨幅接近10%。但国际知名评级机构穆迪发出警告 据悉&#xff0c;到6月25日&#xff…

Unity免费领开发者周6月74个动作捕捉角色运动动画包Omni多方向形状跳跃蹲伏等基础OPSIVE20240628

限时免费领取的角色动画包来啦。在结帐时输入OPSIVE2024优惠券代码即可免费获得。无需购买。 Unity免费领开发者周6月74个动作捕捉角色运动动画包Omni多方向形状跳跃蹲伏等基础OPSIVE202406281136 开发者周促销资源:https://prf.hn/l/w9QJYQ1 优惠券代码:OPSIVE2024 [限时免费…

MIX OTP——监督树和应用

在上一章关于 GenServer 的内容中&#xff0c;我们实现了 KV.Registry 来管理存储容器。在某个时候&#xff0c;我们开始监控存储容器&#xff0c;这样每当 KV.Bucket 崩溃时&#xff0c;我们就能采取行动。虽然变化相对较小&#xff0c;但它提出了一个 Elixir 开发人员经常问的…

Flat Ads:拥抱 CTV,品牌增长新动力

近年来,CTV(联网电视)在数字营销界正迅速崛起,成为最受青睐和增长迅猛的推广形式之一。 随着更多联网设备的普及,越来越多观众正在从传统电视快速转移到流媒体环境,对传统电视广告取而代之的便是 CTV 广告。据 eMarketer 数据显示,未来四年,CTV 市值将从今年的 250 亿美元增长到…

C++-------多态

一.如何实现多态 1.多态的两个条件&#xff1a; (1) 必须通过基类的指针或者引用调用虚函数 (2) 被调用的函数必须是虚函数&#xff0c;且派生类必须对基类的虚函数进行重写&#xff0c;重写必须返回值&#xff0c;函数名&#xff0c;参数类型相同&#xff0c;同时virtual只…

Win10扩充C盘(把其他盘存储空间分给C盘)

C盘虽然没有安装任何软件&#xff0c;但无奈安装某些软件&#xff08;例如VS&#xff0c;QuarC等&#xff09;总会占用C盘容量&#xff0c;且C盘内存很小&#xff08;只有60G左右&#xff09;&#xff0c;看着D盘的三四十空闲内存&#xff0c;决定把D盘内存分给C盘30G&#xff…

uboot中内存DDR测试之mtest使用

相关代码路径: cmd/mem.c配置: make ARCHarm CROSS_COMPILEaarch64-linux-gnu- menuconfigCC cmd/mem.o cmd/mem.c: In function do_mem_mtest: cmd/mem.c:883:10: error: CONFIG_SYS_MEMTEST_START undeclared (first use in this function); did you mean CONFIG_SYS_…

Pbootcms留言“提交成功”的提示语怎么修改

我们在用到pbootcms建站时候&#xff0c;其中有个留言功能&#xff0c;提交成功后会提示&#xff1a;提交成功&#xff08;如下图所示&#xff09;&#xff0c;那么我们要修改这个提示语要怎么操作呢&#xff1f; 如果需要修改的话&#xff0c;直接找到文件/apps/home/control…

一投就中,收稿范围大,1个月内录用,国人发文最多,无风险预警

别人费心费力投个一年都不一定有结果&#xff0c;您直接坐上”直升飞机”&#xff0c;1个月录用。下面老毕分享1本超快录用EI期刊&#xff0c;工程电气方向的学者抓紧投稿。 抢占版面&#xff0c;下方【扫一扫】直接安排&#xff0c;1个月内录用&#x1f308; Journal of Elect…

骗2万人13亿的种菜游戏,幕后老板反手去当了榜一大哥…

这一届人民&#xff0c;可以说&#xff0c;对「种菜」式的田园生活&#xff0c;有一种执念。 或是格子间里日复一日的牢笼生活有些厌倦&#xff0c;也或是过快的城市化进程&#xff0c;让藏在基因里的「田园」属性&#xff0c;还能时不时的觉醒一下…… 除了诗与远方及前些年爆…

Pytest--安装与入门

pytest是一个能够简化成测试系统构建、方便测试规模扩展的框架&#xff0c;它让测试变得更具表现力和可读性–模版代码不再是必需的。只需要几分钟的时间&#xff0c;就可以对你的应用开始一个简单的单元测试或者复杂的功能测试。 1. 安装pytest pip install -U pytest检查版…

PD快充诱骗芯片工作原理,USB-C充电器出不来电压是什么原因?

一般使用Type-C接口的充电器基本上都是采用新的快充协议——PD快充协议&#xff0c;它不同于以前的USB-A的QC协议&#xff0c;这种协议&#xff0c;默认是没有快充电压输出的&#xff0c;VBUS和GND是0V。 所以&#xff0c;我们可以使用电阻的方式&#xff08;电流小&#xff09…

Steam新用户怎么参加夏促 Steam最新注册账号+下载客户端教程

steam夏促来了&#xff0c;这里给新玩家科普一下&#xff0c;steam就是一个游戏平台&#xff0c;里面的海量的各种游戏&#xff0c;而steam经常会有各种打折的活动&#xff0c;夏促就是其中之一&#xff0c;并且是其中规模最大的之一&#xff0c;涵盖游戏数量多&#xff0c;优惠…

【乐吾乐2D可视化组态编辑器】画布

5.1 设置画布属性 默认颜色&#xff1a;预先设置默认颜色&#xff0c;拖拽到画布的节点&#xff08;基础图形、文字、icon&#xff09;自动统一默认颜色。 画笔填充颜色&#xff1a;预先设置画笔填充颜色&#xff0c;拖拽到画布的节点&#xff08;基础图形&#xff09;自动统…

企业应该如果安全上网,软件防查盗版,企业防盗版

随着信息化的发展&#xff0c;企业日常办公越来越依赖互联网。终端以及普通PC终端在访问互联网过程中&#xff0c;会遇到各种各样不容忽视的风险&#xff0c;例如员工主动故意的数据泄漏&#xff0c;后台应用程序偷偷向外部发信息&#xff0c;木马间谍软件的外联&#xff0c;以…