从入门到精通:Linux 系统性能问题定位与解决方案大全

本文针对应用延迟、系统卡顿、偶发频繁卡顿三种常见的系统故障给出通用普适的排查思路。

本文整理了Linux系统性能问题排查的通用方法论和实践,将针对以下三个经典场景展开探讨:

  • I/O性能瓶颈
  • CPU飙升
  • 偶发CPU飙升

同时考虑到笔者文章的受众面大部分都是Java开发人员,所以复现问题故障的例子也都采用Java进行编码部署复现,对应的示例也都会在案例排查的最后展开说明。

一、常见系统巡检流程

1. 查看系统基本运行信息

在正式介绍这些生产案例之前,我们先了解一些比较常见的系统巡检步骤,在日常监控巡检的时候,我们一般先查看系统的运行基本信息,所以我们优先会执行uptime查看系统整体运行情况和负载,以笔者的输出为例,可以看到uptime输出显示如下消息:

  • 系统时间为23:08:23
  • 当前系统运行1天4小时多
  • 当前系统有两个用户登录
  • 系统近1min、15min、30min的系统负载稳定在0

uptime指令可以反映一个时间段的系统运行负载,一般来说,若近1min的值远远低于15和30min的值,这可能就说明我们错过了系统反馈的故障:

23:08:23 up 1 day,  4:02,  3 users,  load average: 0.00, 0.00, 0.00
    2. 查看内核是否存在报错

    然后我们就通过dmesg |tail查看内核环形缓冲区的消息,通过该指令可以看到一些系统消息,检查运行时报错,以笔者服务器的输出结果来看:

    • 网络连接初始化失败
    • hv_balloon动态内存信息
    • Time jumped backwards, rotating即因为某个原因系统向后跳动了
    • 系统缓存清理
    [    5.923407] WSL (206) ERROR: CheckConnection: getaddrinfo() failed: -5
    [   48.414734] hv_balloon: Max. dynamic memory size: 8126 MB
    [   48.590258] systemd-journald[53]: Time jumped backwards, rotating.
    [  605.106029] TCP: eth0: Driver has suspect GRO implementation, TCP performance may be compromised.
    [  676.028375] mini_init (190): drop_caches: 1
    [ 1166.861172] mini_init (190): drop_caches: 1
    [19373.591153] Adjusting hyperv_clocksource_tsc_page more than 11% (1467515026 vs 1862270976)
    [48202.834581] mini_init (190): drop_caches: 1
    [48988.179688] mini_init (190): drop_caches: 1
    [81152.543659] mini_init (190): drop_caches: 1
    3. 查看虚拟内存使用情况

    vmstat用于查看虚拟内存、I/O、CPU运行状态等指标,在进行巡检时我们一般使用vmstat 1进行每隔1秒一次的输出,以观察系统的实时状态。以笔者服务器为例,输出结果如下:

    针对进程的输出参数procs:

    • r: 运行队列中等待运行的进程数,如果该值持续大于CPU核心数,说明CPU资源紧张
    • b: 等待I/O完成的进程数,如果该值持续较高,说明存在I/O瓶颈

    针对内存的参数组memory:

    • swpd: 已使用的交换空间大小,如果该值持续增长,说明物理内存不足
    • free: 空闲的物理内存大小
    • buff: 用于文件系统缓冲的内存大小
    • cache: 用于缓存文件数据的内存大小

    针对磁盘I/O参数组:

    • bi/bo: 每秒从磁盘读取/写入的数据块数
    • si/so: 每秒从磁盘交换进/出内存的数据块数,如果这两个值持续大于0,说明内存不足

    CPU参数组:

    • us: 用户态CPU使用百分比
    • sy: 内核态CPU使用百分比
    • id: CPU空闲百分比
    • wa: 等待I/O完成的CPU时间百分比,如果该值持续较高,说明存在I/O瓶颈
    procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
     0  0      0 7080860  24908 270132    0    0    14    20  520    0  0  0 100  0  0  0
     0  0      0 7080840  24908 270172    0    0     0     0  538  457  0  0 100  0  0  0

    从以上输出可以看出,系统当前状态良好:CPU空闲率100%,无进程等待I/O,无内存交换发生。

    4. CPU亲和力巡检

    CPU亲和力是指进程或线程绑定到特定CPU核心的特性。通过检查各CPU核心的使用率分布,可以判断是否存在CPU负载不均衡的情况。执行mpstat -P ALL 1可以查看每个CPU核心的使用情况。

    从笔者的输出结果来看,各CPU核心的使用率分布相对均匀,没有出现某个CPU核心使用率异常飙升的情况。如果某个CPU核心使用率持续接近100%,而其他核心相对空闲,则可能说明存在以下问题:

    • 单线程应用无法充分利用多核CPU资源
    • 进程调度问题导致负载集中在特定核心
    • 某个线程在特定核心上出现死循环或计算密集型任务
    09:33:20     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    09:33:21     all    0.67    0.00    1.34    0.00    0.00    0.17    0.00    0.00    0.00   97.83
    09:33:21       0    1.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00    0.00   98.00
    09:33:21       1    0.00    0.00    1.01    0.00    0.00    1.01    0.00    0.00    0.00   97.98
    09:33:21       2    1.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.00
    09:33:21       3    0.00    0.00    1.01    0.00    0.00    0.00    0.00    0.00    0.00   98.99
    09:33:21       4    0.00    0.00    2.00    0.00    0.00    0.00    0.00    0.00    0.00   98.00
    09:33:21       5    1.98    0.00    2.97    0.00    0.00    0.00    0.00    0.00    0.00   95.05
    
    09:33:21     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    09:33:22     all    0.17    0.00    0.00    0.00    0.00    0.17    0.00    0.00    0.00   99.67
    09:33:22       0    0.00    0.00    0.00    0.00    0.00    1.00    0.00    0.00    0.00   99.00
    09:33:22       1    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:22       2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:22       3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:22       4    0.99    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.01
    09:33:22       5    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    
    09:33:22     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    09:33:23     all    0.17    0.00    0.17    0.00    0.00    0.00    0.00    0.00    0.00   99.67
    09:33:23       0    1.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.00
    09:33:23       1    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:23       2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:23       3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:23       4    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
    09:33:23       5    0.00    0.00    0.99    0.00    0.00    0.00    0.00    0.00    0.00   99.01
    ^C
    Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
    Average:     all    0.33    0.00    0.50    0.00    0.00    0.11    0.00    0.00    0.00   99.05
    Average:       0    0.67    0.00    0.33    0.00    0.00    0.33    0.00    0.00    0.00   98.67
    Average:       1    0.00    0.00    0.34    0.00    0.00    0.34    0.00    0.00    0.00   99.33
    Average:       2    0.33    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   99.67
    Average:       3    0.00    0.00    0.34    0.00    0.00    0.00    0.00    0.00    0.00   99.66
    Average:       4    0.33    0.00    0.66    0.00    0.00    0.00    0.00    0.00    0.00   99.00
    Average:       5    0.66    0.00    1.32    0.00    0.00    0.00    0.00    0.00    0.00   98.01

    从平均值来看,系统整体CPU使用率较低,各核心负载分布相对均衡,说明CPU资源利用合理。

    5. 检测进程摘要

    pidstat命令用于监控进程的CPU、内存、I/O等资源使用情况。与top命令不同,pidstat不会清屏刷新,而是以滚动方式显示进程信息,便于观察一段时间内的进程负载变化。执行pidstat 1可以每秒输出一次进程统计信息。

    输出字段说明:

    • UID: 进程所属用户ID
    • PID: 进程ID
    • %usr: 进程在用户态消耗的CPU百分比
    • %system: 进程在内核态消耗的CPU百分比
    • %guest: 进程在虚拟机中消耗的CPU百分比
    • %wait: 进程等待CPU的时间百分比
    • %CPU: 进程总的CPU使用百分比
    • CPU: 进程当前运行的CPU核心编号
    • Command: 进程命令名称
    Linux 6.6.87.2-microsoft-standard-WSL2 (DESKTOP-0F6E7K1)        09/15/25        _x86_64_        (6 CPU)
    
    09:35:48      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    09:35:49      105       267    0.00    1.00    0.00    0.00    1.00     0  mysqld
    
    09:35:49      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    09:35:50      105       267    1.00    0.00    0.00    0.00    1.00     0  mysqld
    
    09:35:50      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    09:35:51        0       171    1.00    0.00    0.00    0.00    1.00     4  frpc
    
    09:35:51      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    09:35:52      105       267    1.00    0.00    0.00    0.00    1.00     0  mysqld
    ^C
    
    Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
    Average:        0       171    0.25    0.00    0.00    0.00    0.25     -  frpc
    Average:      105       267    0.50    0.25    0.00    0.00    0.75     -  mysqld

    从输出结果可以看出:

    • mysqld进程(PID 267)在系统态有较高的CPU使用率,说明该进程主要进行内核态操作
    • frpc进程(PID 171)在用户态有CPU使用,说明该进程主要进行用户态计算
    • 从平均值来看,两个进程的CPU使用率都不高,系统负载较轻
    6. 查看内存使用情况

    free命令用于查看系统内存使用情况,包括物理内存和交换空间的使用统计。一般情况下我们使用free -m以MB为单位查看内存使用情况。

    输出字段说明:

    • total: 内存总量
    • used: 已使用的内存
    • free: 空闲的内存
    • shared: 多个进程共享的内存
    • buff/cache: 用于缓冲和缓存的内存
    • available: 可用内存(包括可回收的缓存和缓冲区内存)
    total        used        free      shared  buff/cache   available
    Mem:            7876         828        6915           3         289        7048
    Swap:           2048           0        2048

    从输出结果可以看出:

    • 物理内存总量为7876MB,已使用828MB,空闲6915MB,内存使用率较低
    • buff/cache占用289MB,这部分内存可以在需要时被回收
    • available内存为7048MB,说明系统有充足的可用内存
    • 交换空间总量2048MB,已使用0MB,说明物理内存充足,未发生内存交换

    需要注意的是,Linux系统会尽可能利用空闲内存作为文件系统缓存,因此free值较低并不一定表示内存不足。应重点关注available字段和交换空间使用情况来判断内存是否充足。

    7. 查看I/O使用情况

    iostat命令用于监控系统设备的I/O负载情况。使用iostat -xz 1可以详细查看磁盘的读写性能指标,其中:

    • -x: 显示扩展统计信息
    • -z: 跳过无活动的设备
    • 1: 每秒刷新一次

    主要监控指标包括:

    • r/s、w/s: 每秒读/写请求数
    • rkB/s、wkB/s: 每秒读/写的数据量(KB)
    • await: I/O请求的平均等待时间(ms),包括排队时间和处理时间
    • avgqu-sz: 平均I/O请求队列长度,如果大于1说明设备可能已饱和
    • util: 设备使用率百分比,如果持续高于80%说明设备可能成为瓶颈

    从输出结果可以看出:

    • 各磁盘设备的I/O请求很少,r/s和w/s值都很低
    • await值都很小,说明I/O响应时间很短
    • util值都很低,说明磁盘设备使用率很低,没有出现瓶颈
    • avgqu-sz值都小于1,说明I/O请求队列长度正常

    整体来看,系统磁盘I/O性能良好,没有出现性能瓶颈。

    Linux 6.6.87.2-microsoft-standard-WSL2 (DESKTOP-0F6E7K1)        09/14/25        _x86_64_        (6 CPU)
    
    avg-cpu:  %user   %nice %system %iowait  %steal   %idle
               0.09    0.00    0.19    0.00    0.00   99.72
    
    Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util
    sda              0.01      0.72     0.00  27.46    0.40    65.57    0.00      0.00     0.00   0.00    0.00     0.00    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    0.00   0.00
    sdb              0.00      0.04     0.00  36.65    0.50    35.94    0.00      0.00     0.00   0.00    0.00     0.00    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    0.00   0.00
    sdc              0.00      0.02     0.00   0.00    0.08    22.73    0.00      0.00     0.00   0.00    1.50     2.00    0.00      0.00     0.00   0.00    0.00     0.00    0.00    2.00    0.00   0.00
    sdd              0.23     13.47     0.17  42.55    0.36    59.20    2.73     20.75     1.07  28.22    0.61     7.60    0.20    122.27     0.00   1.74    0.19   611.44    0.18    0.66    0.00   0.29
    8. 查看网卡使用情况

    sar命令可以监控网络接口的流量情况。使用sar -n DEV 1可以每秒输出网络接口的统计信息。

    输出字段说明:

    • IFACE: 网络接口名称
    • rxpck/s: 每秒接收的数据包数量
    • txpck/s: 每秒发送的数据包数量
    • rxkB/s: 每秒接收的数据量(KB)
    • txkB/s: 每秒发送的数据量(KB)
    • %ifutil: 网络接口使用率百分比
    23:38:11        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
    23:38:12           lo      7.00      7.00      0.68      0.68      0.00      0.00      0.00      0.00
    23:38:12         eth0      9.00      7.00      0.81      0.72      0.00      0.00      0.00      0.00
    
    23:38:12        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
    23:38:13           lo     18.81     18.81      5.98      5.98      0.00      0.00      0.00      0.00
    23:38:13         eth0     12.87     13.86      1.31      5.92      0.00      0.00      0.00      0.00
    
    23:38:13        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
    23:38:14           lo     17.00     17.00      1.60      1.60      0.00      0.00      0.00      0.00
    23:38:14         eth0     11.00      8.00      0.97      1.33      0.00      0.00      0.00      0.00

    从输出结果可以看出:

    • lo接口是本地回环接口,用于本机通信
    • eth0是实际的网络接口
    • 数据包收发量和数据流量都比较小
    • %ifutil值为0,说明网络接口使用率很低,没有出现网络瓶颈

    为了更准确地判断网络性能,还需要结合网络带宽信息进行分析。

    上述指标反映了网络I/O的整体情况,实际上我们可能还需要结合带宽进行判断。ethtool命令可以查看网卡的详细信息,包括连接速度、双工模式等。

    首先需要安装ethtool工具:

    sudo apt install ethtool

      然后通过ethtool eth0查看网卡的带宽连接速度:

      Settings for eth0:
              Supported ports: [  ]
              Supported link modes:   Not reported
              Supported pause frame use: No
              Supports auto-negotiation: No
              Supported FEC modes: Not reported
              Advertised link modes:  Not reported
              Advertised pause frame use: No
              Advertised auto-negotiation: No
              Advertised FEC modes: Not reported
              Speed: 10000Mb/s
              Duplex: Full
              Auto-negotiation: off
              Port: Other
              PHYAD: 0
              Transceiver: internal
      netlink error: Operation not permitted
              Current message level: 0x000000f7 (247)
                                     drv probe link ifdown ifup rx_err tx_err
              Link detected: yes

      从输出结果可以看出:

      • 网卡连接速度为10000Mb/s(即10Gb/s),这是一个很高的带宽
      • 工作模式为全双工(Full Duplex)
      • Link detected: yes 表示网络连接正常

      结合之前的sar输出数据,eth0接口的rxkB/s和txkB/s值都很小,远低于10Gb/s的带宽上限,说明网络带宽使用率很低,网络资源充足。

      二、详解生产环境常见问题

      1. 应用程序延迟升高

      第一个案例是用户反馈系统延迟升高,网卡打开缓慢。从开发者的角度一定要明白,所有表现为卡顿、延迟的原因很大概率是系统资源吃紧,只有在资源分配不足的情况下,才会导致程序运行阻塞,无法及时处理用户的请求。

      关于服务器的阈值指标,按照业界通用的经验,对应CPU和内存的负载要求的合理上限为:

      • CPU使用率控制在75%左右
      • 内存使用率控制在80%以内
      • 虚拟内存尽可能保持在0%
      • 负载不超过CPU核心数的75%

      笔者一般会先通过top查看操作系统的CPU利用率,这里笔者因为是个人服务器原因则采用更强大、更直观的htop查看个人服务器的资源使用情况,对应的安装指令如下:

      sudo apt update
      sudo apt install htop

      而本次htop输出的指标如下:

      • 服务器为6核,对应的CPU使用率分别是3.9、0、0、2.7、0.7、1.4,按照业界的通用标准,当前服务器各核心CPU使用率较低,但需结合系统负载综合判断
      • Mem代表了内存使用率,内存一般情况下是用于存储程序代码和数据,分为物理内存和虚拟内存,物理内存显示内存接近8G仅用了1G不到,使用率不到80%,说明资源冗余
      • Swp显示交换空间即虚拟内存的使用情况,可以看到也仅仅用了32M,并没有大量的内存数据被置换到交换空间,结合第2点来看,内存资源充足
      • Tasks显示进程数和线程数一共有35个进程,这35个进程对应100个线程处理,Kthr显示指标为0说明有0个内核线程,而Running为1说明有一个用户进程在运行
      • 而系统平均负载近1分钟为4.96,按照业界标准CPU核心数*0.75作为系统负载的运算阈值,如果超过这个值则说明系统处于高负载状态,很明显我们的6核服务器系统负载偏高了

      综合来看,服务器系统负载偏高但各CPU核心使用率较低,结合内存使用情况,问题可能出现在I/O资源等待上,此时我们就要从I/O资源角度进一步排查问题:

      我们从I/O资源排查入手,通过vmstat 1执行每秒一次的监控指标输出,以笔者的服务器为例,可以看到如下几个指标:

      • r:按照文档解释为The number of runnable processes (running or waiting for run time)即正在运行或等待运行的进程数,如果大于CPU核心数则说明CPU处于过载状态,而当前服务器这个值为0,说明队列处理状态良好
      • b: 按照文档解释为The number of processes blocked waiting for I/O to complete即等待I/O完成的进程数,从参数b可以看出有大量进程等待I/O,说明当前服务器存在I/O瓶颈。
      • swpd: the amount of swap memory used即交换空间也就是虚拟内存的使用,而当前服务器已被使用30468说明已经在使用交换空间,由此参数结合buff(缓存中尚未写入磁盘的内容)和cache(从磁盘加载出来的缓存数据)来看,当前内存资源持续升高,存在读写虚拟内存的情况,存在I/O性能瓶颈。
      • 从bo来看有大量任务进行每秒写块
      • 针对CPU一个板块输出的us(用户代码执行时间)、sy(内核执行时间)、id(空闲时间)、wa(等待I/O的时间),其中wa即等待I/O的时间持续处于一个高数值的状态,更进一步明确CPU在空转,等待I/O完成,而I/O资源处于吃紧的状态

      考虑为I/O资源瓶颈,我们优先从网络I/O角度排查问题,这里笔者采用nload进行网络资源诊断,如果没有下载的可以自行通过yum或者apt的方式自行下载,这里笔者也贴出ubuntu服务器的下载指令:

      # ubuntu下载安装nload
      sudo apt install nload -y

      键入nload实时输出网络带宽的使用情况,可以看到:

      • 输入流量(incoming)即下载流量,当前下载速度约为1KB/s,占最大带宽的20%左右,一般认为只有当网速接近最大带宽时才说明带宽使用率接近饱和
      • 输出流量(outgoing)即上传流量,同理当前也仅仅使用8%,也未达到饱和的阈值

      所以I/O资源吃紧的问题原因并非网络I/O,我们需要进一步从服务器磁盘I/O角度进一步排查:

      所以从这些指标来看,存在大量的线程在等待I/O资源的分配而进入阻塞,所以笔者基于iostat -x 1使每一秒都输出更详细的信息,可以看到sdd盘对应的磁盘忙碌百分比util基本跑满100%,已基本接近饱和,此时基本是确认有大量线程在进行I/O读写任务了,且查看I/O读写指标:

      • 每秒读写的吞吐量w/s为175
      • 每秒写入wkB/s的172704KB
      • SSD盘util即I/O资源利用率为100%,已经远超业界阈值60%,说明存在I/O性能瓶颈,需要补充说明的是当CPU和I/O设备达到100%利用率时,都可能导致进程阻塞,但I/O设备的处理机制与CPU调度不同,在高负载情况下更容易导致大量I/O请求排队等待
      • 写请求的平均等待时间w_await为5151ms

      换算下来172704KB/175每秒写入的速率为987KB每秒,由此可确定因为磁盘性能读写性能瓶颈导致大量I/O读写任务阻塞,进而导致服务器卡顿,用户体验差:

      所以,对于系统延迟严重的情况,整体排查思路为:

      • 通过top指令查看CPU使用率,若正常进入步骤2
      • 基于vmstat查看内存使用率和I/O资源情况
      • 基于nload查看网络I/O使用情况
      • 基于iostat查看网络I/O和磁盘I/O情况最终确认问题

      本例子的最后笔者也给出本次故障问题的示例代码:

      /**
           * 启动磁盘I/O操作以模拟高I/O负载
           * 通过创建多个I/O任务线程来模拟高磁盘I/O负载
           */
          private static void startDiskIOOperations() {
              log.info("开始高I/O磁盘操作...");
              log.info("在另一个终端中运行 'iostat -x 1' 来监控磁盘利用率。");
      
              // 创建固定线程数的线程池
              executor = Executors.newFixedThreadPool(NUM_THREADS);
      
              // 提交多个任务以连续写入磁盘
              for (int i = 0; i < NUM_THREADS; i++) {
                  executor.submit(new IOTask(i));
              }
      
              log.info("磁盘I/O操作已启动,使用 {} 个线程", NUM_THREADS);
          }
      
      
      /**
           * 执行连续写入操作以模拟高I/O的任务
           * 该类负责执行磁盘I/O操作,通过不断写入和清空文件来模拟高I/O负载
           */
          static class IOTask implements Runnable {
              private final int taskId;
      
              public IOTask(int taskId) {
                  this.taskId = taskId;
              }
      
              @Override
              public void run() {
                  // 每个线程写入自己的临时文件
                  String filename = "/tmp/disk_io_test_" + taskId + ".tmp";
      
                  try (FileOutputStream fos = new FileOutputStream(filename)) {
                      log.info("线程-{} 正在写入 {}", taskId, filename);
      
                      // 连续将数据写入文件并在每次写入后清空文件
                      while (!Thread.currentThread().isInterrupted()) {
                          performDiskIOOperation(fos, taskId);
                          ThreadUtil.sleep(500);
                      }
                  } catch (IOException e) {
                      log.error("线程-{} 发生错误: {}", taskId, e.getMessage());
                  }
              }
          }
      2. 系统操作卡顿

      第二个例子也同样是用户反馈系统操作卡顿感严重,整体点击响应非常慢,我们还是考虑资源吃紧,优先使用top指令查看资源使用情况,从top指令来看:

      • 输出us查看各个核心的CPU使用率跑满
      • 系统平均负载基本超过70%(6*0.7)已经超过4.2

      这就是经典的计算密集型任务跑满所有线程的经典例子

      一般针对CPU跑满的问题,笔者一般会通过mpstat -P ALL 1查看CPU时间片是否分配均衡,是否出现偏斜导致CPU过热的情况,例如所有运算任务都往一个CPU核心上跑,经过笔者每秒1次的输出持续观察来看,整体资源吃紧,但并没有出现资源分配偏斜的情况,同时内存资源使用率也不高,也没有大量的iowait等待:

      结合第一步top指令定位到的进程是Java进程,笔者索性通过Arthas直接定位故障代码,首先通过thread锁定问题线程,可以看到pool-前缀的线程基本都是跑满单个CPU,所以我们就可以通过thread id查看线程的栈帧:

      最终锁定到了这段代码段,即一个密集的循环运算的线程:

      对应的笔者也贴出故障代码段示例,来总结一下系统使用卡顿的排查思路:

      • 基本top查看用户态和内核态CPU使用率
      • 用户态使用率偏高,通过mpstat查看CPU使用是否偏斜,是否保证CPU亲和力
      • 如果CPU使用没有出现偏斜,则直接通过问题定位到Java进程,结合Arthas快速定位问题线程进行诊断
      /**
           * 模拟CPU使用率过高的情况
           * 通过创建多个CPU密集型任务线程来模拟高CPU使用率
           */
          public static void startHighCPUUsage() {
              log.info("开始模拟高CPU使用率...");
      
              // 创建CPU密集型任务的线程池
              ExecutorService cpuExecutor = Executors.newFixedThreadPool(NUM_THREADS);
      
              // 提交多个CPU密集型任务
              for (int i = 0; i < NUM_THREADS; i++) {
                  cpuExecutor.submit(new CPUIntensiveTask(i));
              }
      
              log.info("高CPU使用率模拟已启动,使用 {} 个线程", NUM_THREADS);
          }
       /**
           * CPU密集型任务,用于模拟高CPU使用率
           * 该类通过执行复杂的数学计算来占用CPU资源,从而模拟高CPU使用率场景
           */
          static class CPUIntensiveTask implements Runnable {
              private final int taskId;
      
              public CPUIntensiveTask(int taskId) {
                  this.taskId = taskId;
              }
      
              @Override
              public void run() {
                  log.info("CPU密集型任务-{} 已启动", taskId);
      
                  // 执行CPU密集型计算以提高CPU使用率
                  while (!Thread.currentThread().isInterrupted()) {
                      // 执行一些复杂的数学计算
                      double result = 0;
                      for (int i = 0; i < 1000000; i++) {
                          result += Math.sqrt(Math.log(i + 1) * Math.cos(i) * Math.sin(i));
                      }
                      log.debug("CPU任务-{} 完成一轮计算,结果: {}", taskId, result);
                  }
      
                  log.info("CPU密集型任务-{} 已结束", taskId);
              }
          }
      3. 持续的偶发性系统卡顿问题排查

      此类问题比较棘手,系统偶发卡顿意味着是瞬时、频繁的资源吃紧,我们还是直接使用top指令无法明确立刻捕捉到进程,可能刚刚一看到飙升的进程就消失了。

      同理使用mpstat、vmstat指令无法准确定位到超短期飙升问题的进程,而基于iostat也没有看到明显的I/O资源吃紧,所以我们可以采用perf指令解决问题,以笔者的Ubuntu服务器为例,对应的安装步骤:

      # 安装perf工具
      sudo apt install linux-tools-generic
      sudo apt install linux-tools-common

      在笔者完成安装并启动之后,系统抛出WARNING: perf not found for kernel xxxx的异常,对应的解决方案是要主动安装linux-tools-generic并定位到linux-tools目录下找到自己的generic版本完成符号链接,以笔者本次安装为例就是6.8.0-79:

      sudo ln -s /usr/lib/linux-tools/6.8.0-79-generic/perf /usr/bin/perf

        完成上述安装之后,我们就可以通过将频率降低设置为99并将监控结果导出到tmp目录下的perf.data中:

        sudo perf record -F 99 -a -g -o /tmp/perf.data sleep 10

          可能很多读者好奇为什么需要将频率设置为99Hz,这样做的目的是为了避免与系统定时器中断频率(通常为100Hz)同步,从而避免锁步采样导致的偏差。

          锁步采样是指采样频率与系统定时器中断频率相同或成倍数关系时,采样点会固定在相同的时间位置上,导致采样结果不能准确反映系统整体的性能状况。

          使用99Hz这样的素数频率可以减少与系统周期性活动同步的概率,从而获得更全面、更准确的性能数据。

          举个简单的例子,若我们试图确定道路是否出现拥堵,且通过24h一次的抽检查,那么当前样本就可能与交通保持一个平行的同步状态,例如:

          • 交通车流情况在每天8点-12点拥堵,而我们的程序也是恰好在每天9点采集,那么它就会认为交通情况异常拥堵
          • 若每天14点进行一次采集那么就避开了交通阻塞的高峰期则会得到一个相反的、也是不正确的结论

          为了规避相同周期频率导致的lockstep即锁同步采样,我们可以适当降低频率避免与交通周期时间同步,保证一天的数据能够在一个周期内被完整地采集到,而本例最好的做法就是将定时间隔改为23h,这样一来每个23天的样本周期就会得到一天中所有时间的数据就能做到全面地了解到交通情况:

          等待片刻后perf指令就会将结果输出到perf.data目录下:

          [ perf record: Woken up 1 times to write data ]
          [ perf record: Captured and wrote 0.701 MB /tmp/perf.data (586 samples) ]

          此时,通过sudo perf report查看报告,可以看到一个pid为1115751的Java进程对应线程CPU使用率飙升到86,此时我们就可以基于这条信息到指定的进程上查看该线程是否存在密集的运算:

          最后我们也给出本示例的问题代码:

          /**
               * 模拟CPU瞬间飙高然后降低的情况
               * 实现每10秒一次的CPU使用率飙高和降低循环(仅使用单核)
               */
              public static void startCPUSpikeAndDrop() {
                  log.info("开始模拟CPU瞬间飙高然后降低...");
          
                  // 创建用于CPU飙高的线程池(仅使用单核)
                  ExecutorService spikeExecutor = Executors.newFixedThreadPool(1);
          
                  // 提交单个CPU密集型任务来制造飙高
                  spikeExecutor.submit(new CPUSpikeTask(0));
          
                  log.info("CPU瞬间飙高已启动,使用 {} 个线程", 1);
          
                  // 每隔10秒切换CPU飙高状态,实现周期性飙高和降低
                  Thread spikeController = new Thread(() -> {
                      boolean isSpiking = true;
                      ExecutorService currentExecutor = spikeExecutor;
          
                      while (!Thread.currentThread().isInterrupted()) {
                          try {
                              // 等待10秒
                              Thread.sleep(10000);
          
                              if (isSpiking) {
                                  // 停止当前的CPU飙高任务
                                  currentExecutor.shutdownNow();
                                  log.info("CPU使用率已降低");
                              } else {
                                  // 启动新的CPU飙高任务
                                  currentExecutor = Executors.newFixedThreadPool(1);
                                  currentExecutor.submit(new CPUSpikeTask(0));
                                  log.info("CPU使用率已飙高");
                              }
          
                              // 切换状态
                              isSpiking = !isSpiking;
                          } catch (InterruptedException e) {
                              log.error("CPU飙高控制线程被中断", e);
                              break;
                          }
                      }
                  });
          
                  spikeController.setDaemon(true);
                  spikeController.start();
              }
           /**
               * CPU瞬间飙高任务,用于模拟CPU瞬间飙高然后降低的情况
               * 该类通过执行密集的数学计算来模拟CPU使用率的瞬时飙高,并在指定时间后自动停止
               */
              static class CPUSpikeTask implements Runnable {
                  private final int taskId;
          
                  public CPUSpikeTask(int taskId) {
                      this.taskId = taskId;
                  }
          
                  @Override
                  public void run() {
                      log.info("CPU瞬间飙高任务-{} 已启动", taskId);
          
                      // 执行空循环以提高CPU使用率
                      while (!Thread.currentThread().isInterrupted()) {
                          // 空循环消耗CPU
                      }
          
                      log.info("CPU瞬间飙高任务-{} 已结束", taskId);
                  }
              }

          三、小结

          本文针对应用延迟、系统卡顿、偶发频繁卡顿三种常见的系统故障给出通用普适的排查思路,整体来说此类问题归根结底都是系统资源吃紧,需要找到饱和的资源结合代码推测根源并制定修复策略,以本文为例,通用的排查思路都为:

          • 基于top查看CPU、内存、负载
          • 若CPU未饱和则通过vmstat查看I/O资源使用情况
          • 明确I/O瓶颈通过nload和iostat查询是网络I/O还是磁盘I/O
          • 若上述排查都无果,且CPU负载偶发飙高,可通过perf并调整频率监控系统定位系统中异常运行的资源
          • 结合上述推断结果查看是否是异常消耗,如果是则优化代码,反之结合情况增加硬件资源

          此外,对于内存相关问题,还可以通过以下方式进一步诊断:

          • 使用ps命令查看进程的内存使用情况,特别关注RSS(常驻内存)和VSZ(虚拟内存)字段
          • 使用pmap命令查看进程的内存映射情况,识别是否存在异常的内存段
          • 使用valgrind等工具检测C/C++程序的内存泄漏问题
          • 对于Java应用,可结合jstat、jmap等工具分析堆内存使用情况

          AI大模型学习福利

          作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

          一、全套AGI大模型学习路线

          AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

          因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

          二、640套AI大模型报告合集

          这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

          因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

          三、AI大模型经典PDF籍

          随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


          因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

          四、AI大模型商业化落地方案

          因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

          作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

          评论
          添加红包

          请填写红包祝福语或标题

          红包个数最小为10个

          红包金额最低5元

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

          抵扣说明:

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

          余额充值