從入門到精通:Linux 系統(tǒng)性能問題定位與解決方案大全
本文整理了Linux系統(tǒng)性能問題排查的通用方法論和實踐,將針對以下三個經(jīng)典場景展開探討:
- I/O性能瓶頸
- CPU飆升
- 偶發(fā)CPU飆升
同時考慮到筆者文章的受眾面大部分都是Java開發(fā)人員,所以復(fù)現(xiàn)問題故障的例子也都采用Java進(jìn)行編碼部署復(fù)現(xiàn),對應(yīng)的示例也都會在案例排查的最后展開說明。

一、常見系統(tǒng)巡檢流程
1. 查看系統(tǒng)基本運行信息
在正式介紹這些生產(chǎn)案例之前,我們先了解一些比較常見的系統(tǒng)巡檢步驟,在日常監(jiān)控巡檢的時候,我們一般先查看系統(tǒng)的運行基本信息,所以我們優(yōu)先會執(zhí)行uptime查看系統(tǒng)整體運行情況和負(fù)載,以筆者的輸出為例,可以看到uptime輸出顯示如下消息:
- 系統(tǒng)時間為23:08:23
- 當(dāng)前系統(tǒng)運行1天4小時多
- 當(dāng)前系統(tǒng)有兩個用戶登錄
- 系統(tǒng)近1min、15min、30min的系統(tǒng)負(fù)載穩(wěn)定在0
uptime指令可以反映一個時間段的系統(tǒng)運行負(fù)載,一般來說,若近1min的值遠(yuǎn)遠(yuǎn)低于15和30min的值,這可能就說明我們錯過了系統(tǒng)反饋的故障:
23:08:23 up 1 day, 4:02, 3 users, load average: 0.00, 0.00, 0.002. 查看內(nèi)核是否存在報錯
然后我們就通過dmesg |tail查看內(nèi)核環(huán)形緩沖區(qū)的消息,通過該指令可以看到一些系統(tǒng)消息,檢查運行時報錯,以筆者服務(wù)器的輸出結(jié)果來看:
- 網(wǎng)絡(luò)連接初始化失敗
- hv_balloon動態(tài)內(nèi)存信息
- Time jumped backwards, rotating即因為某個原因系統(tǒng)向后跳動了
- 系統(tǒng)緩存清理
[ 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: 13. 查看虛擬內(nèi)存使用情況
vmstat用于查看虛擬內(nèi)存、I/O、CPU運行狀態(tài)等指標(biāo),在進(jìn)行巡檢時我們一般使用vmstat 1進(jìn)行每隔1秒一次的輸出,以觀察系統(tǒng)的實時狀態(tài)。以筆者服務(wù)器為例,輸出結(jié)果如下:
針對進(jìn)程的輸出參數(shù)procs:
- r: 運行隊列中等待運行的進(jìn)程數(shù),如果該值持續(xù)大于CPU核心數(shù),說明CPU資源緊張
- b: 等待I/O完成的進(jìn)程數(shù),如果該值持續(xù)較高,說明存在I/O瓶頸
針對內(nèi)存的參數(shù)組memory:
- swpd: 已使用的交換空間大小,如果該值持續(xù)增長,說明物理內(nèi)存不足
- free: 空閑的物理內(nèi)存大小
- buff: 用于文件系統(tǒng)緩沖的內(nèi)存大小
- cache: 用于緩存文件數(shù)據(jù)的內(nèi)存大小
針對磁盤I/O參數(shù)組:
- bi/bo: 每秒從磁盤讀取/寫入的數(shù)據(jù)塊數(shù)
- si/so: 每秒從磁盤交換進(jìn)/出內(nèi)存的數(shù)據(jù)塊數(shù),如果這兩個值持續(xù)大于0,說明內(nèi)存不足
CPU參數(shù)組:
- us: 用戶態(tài)CPU使用百分比
- sy: 內(nèi)核態(tài)CPU使用百分比
- id: CPU空閑百分比
- wa: 等待I/O完成的CPU時間百分比,如果該值持續(xù)較高,說明存在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從以上輸出可以看出,系統(tǒng)當(dāng)前狀態(tài)良好:CPU空閑率100%,無進(jìn)程等待I/O,無內(nèi)存交換發(fā)生。
4. CPU親和力巡檢
CPU親和力是指進(jìn)程或線程綁定到特定CPU核心的特性。通過檢查各CPU核心的使用率分布,可以判斷是否存在CPU負(fù)載不均衡的情況。執(zhí)行mpstat -P ALL 1可以查看每個CPU核心的使用情況。
從筆者的輸出結(jié)果來看,各CPU核心的使用率分布相對均勻,沒有出現(xiàn)某個CPU核心使用率異常飆升的情況。如果某個CPU核心使用率持續(xù)接近100%,而其他核心相對空閑,則可能說明存在以下問題:
- 單線程應(yīng)用無法充分利用多核CPU資源
- 進(jìn)程調(diào)度問題導(dǎo)致負(fù)載集中在特定核心
- 某個線程在特定核心上出現(xiàn)死循環(huán)或計算密集型任務(wù)
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從平均值來看,系統(tǒng)整體CPU使用率較低,各核心負(fù)載分布相對均衡,說明CPU資源利用合理。
5. 檢測進(jìn)程摘要
pidstat命令用于監(jiān)控進(jìn)程的CPU、內(nèi)存、I/O等資源使用情況。與top命令不同,pidstat不會清屏刷新,而是以滾動方式顯示進(jìn)程信息,便于觀察一段時間內(nèi)的進(jìn)程負(fù)載變化。執(zhí)行pidstat 1可以每秒輸出一次進(jìn)程統(tǒng)計信息。
輸出字段說明:
- UID: 進(jìn)程所屬用戶ID
- PID: 進(jìn)程ID
- %usr: 進(jìn)程在用戶態(tài)消耗的CPU百分比
- %system: 進(jìn)程在內(nèi)核態(tài)消耗的CPU百分比
- %guest: 進(jìn)程在虛擬機(jī)中消耗的CPU百分比
- %wait: 進(jìn)程等待CPU的時間百分比
- %CPU: 進(jìn)程總的CPU使用百分比
- CPU: 進(jìn)程當(dāng)前運行的CPU核心編號
- Command: 進(jìn)程命令名稱
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從輸出結(jié)果可以看出:
- mysqld進(jìn)程(PID 267)在系統(tǒng)態(tài)有較高的CPU使用率,說明該進(jìn)程主要進(jìn)行內(nèi)核態(tài)操作
- frpc進(jìn)程(PID 171)在用戶態(tài)有CPU使用,說明該進(jìn)程主要進(jìn)行用戶態(tài)計算
- 從平均值來看,兩個進(jìn)程的CPU使用率都不高,系統(tǒng)負(fù)載較輕
6. 查看內(nèi)存使用情況
free命令用于查看系統(tǒng)內(nèi)存使用情況,包括物理內(nèi)存和交換空間的使用統(tǒng)計。一般情況下我們使用free -m以MB為單位查看內(nèi)存使用情況。
輸出字段說明:
- total: 內(nèi)存總量
- used: 已使用的內(nèi)存
- free: 空閑的內(nèi)存
- shared: 多個進(jìn)程共享的內(nèi)存
- buff/cache: 用于緩沖和緩存的內(nèi)存
- available: 可用內(nèi)存(包括可回收的緩存和緩沖區(qū)內(nèi)存)
total used free shared buff/cache available
Mem: 7876 828 6915 3 289 7048
Swap: 2048 0 2048從輸出結(jié)果可以看出:
- 物理內(nèi)存總量為7876MB,已使用828MB,空閑6915MB,內(nèi)存使用率較低
- buff/cache占用289MB,這部分內(nèi)存可以在需要時被回收
- available內(nèi)存為7048MB,說明系統(tǒng)有充足的可用內(nèi)存
- 交換空間總量2048MB,已使用0MB,說明物理內(nèi)存充足,未發(fā)生內(nèi)存交換
需要注意的是,Linux系統(tǒng)會盡可能利用空閑內(nèi)存作為文件系統(tǒng)緩存,因此free值較低并不一定表示內(nèi)存不足。應(yīng)重點關(guān)注available字段和交換空間使用情況來判斷內(nèi)存是否充足。
7. 查看I/O使用情況
iostat命令用于監(jiān)控系統(tǒng)設(shè)備的I/O負(fù)載情況。使用iostat -xz 1可以詳細(xì)查看磁盤的讀寫性能指標(biāo),其中:
- -x: 顯示擴(kuò)展統(tǒng)計信息
- -z: 跳過無活動的設(shè)備
- 1: 每秒刷新一次
主要監(jiān)控指標(biāo)包括:
- r/s、w/s: 每秒讀/寫請求數(shù)
- rkB/s、wkB/s: 每秒讀/寫的數(shù)據(jù)量(KB)
- await: I/O請求的平均等待時間(ms),包括排隊時間和處理時間
- avgqu-sz: 平均I/O請求隊列長度,如果大于1說明設(shè)備可能已飽和
- util: 設(shè)備使用率百分比,如果持續(xù)高于80%說明設(shè)備可能成為瓶頸
從輸出結(jié)果可以看出:
- 各磁盤設(shè)備的I/O請求很少,r/s和w/s值都很低
- await值都很小,說明I/O響應(yīng)時間很短
- util值都很低,說明磁盤設(shè)備使用率很低,沒有出現(xiàn)瓶頸
- avgqu-sz值都小于1,說明I/O請求隊列長度正常
整體來看,系統(tǒng)磁盤I/O性能良好,沒有出現(xiàn)性能瓶頸。
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.298. 查看網(wǎng)卡使用情況
sar命令可以監(jiān)控網(wǎng)絡(luò)接口的流量情況。使用sar -n DEV 1可以每秒輸出網(wǎng)絡(luò)接口的統(tǒng)計信息。
輸出字段說明:
- IFACE: 網(wǎng)絡(luò)接口名稱
- rxpck/s: 每秒接收的數(shù)據(jù)包數(shù)量
- txpck/s: 每秒發(fā)送的數(shù)據(jù)包數(shù)量
- rxkB/s: 每秒接收的數(shù)據(jù)量(KB)
- txkB/s: 每秒發(fā)送的數(shù)據(jù)量(KB)
- %ifutil: 網(wǎng)絡(luò)接口使用率百分比
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從輸出結(jié)果可以看出:
- lo接口是本地回環(huán)接口,用于本機(jī)通信
- eth0是實際的網(wǎng)絡(luò)接口
- 數(shù)據(jù)包收發(fā)量和數(shù)據(jù)流量都比較小
- %ifutil值為0,說明網(wǎng)絡(luò)接口使用率很低,沒有出現(xiàn)網(wǎng)絡(luò)瓶頸
為了更準(zhǔn)確地判斷網(wǎng)絡(luò)性能,還需要結(jié)合網(wǎng)絡(luò)帶寬信息進(jìn)行分析。
上述指標(biāo)反映了網(wǎng)絡(luò)I/O的整體情況,實際上我們可能還需要結(jié)合帶寬進(jìn)行判斷。ethtool命令可以查看網(wǎng)卡的詳細(xì)信息,包括連接速度、雙工模式等。
首先需要安裝ethtool工具:
sudo apt install ethtool然后通過ethtool eth0查看網(wǎng)卡的帶寬連接速度:
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從輸出結(jié)果可以看出:
- 網(wǎng)卡連接速度為10000Mb/s(即10Gb/s),這是一個很高的帶寬
- 工作模式為全雙工(Full Duplex)
- Link detected: yes 表示網(wǎng)絡(luò)連接正常
結(jié)合之前的sar輸出數(shù)據(jù),eth0接口的rxkB/s和txkB/s值都很小,遠(yuǎn)低于10Gb/s的帶寬上限,說明網(wǎng)絡(luò)帶寬使用率很低,網(wǎng)絡(luò)資源充足。
二、詳解生產(chǎn)環(huán)境常見問題
1. 應(yīng)用程序延遲升高
第一個案例是用戶反饋系統(tǒng)延遲升高,網(wǎng)卡打開緩慢。從開發(fā)者的角度一定要明白,所有表現(xiàn)為卡頓、延遲的原因很大概率是系統(tǒng)資源吃緊,只有在資源分配不足的情況下,才會導(dǎo)致程序運行阻塞,無法及時處理用戶的請求。
關(guān)于服務(wù)器的閾值指標(biāo),按照業(yè)界通用的經(jīng)驗,對應(yīng)CPU和內(nèi)存的負(fù)載要求的合理上限為:
- CPU使用率控制在75%左右
- 內(nèi)存使用率控制在80%以內(nèi)
- 虛擬內(nèi)存盡可能保持在0%
- 負(fù)載不超過CPU核心數(shù)的75%
筆者一般會先通過top查看操作系統(tǒng)的CPU利用率,這里筆者因為是個人服務(wù)器原因則采用更強大、更直觀的htop查看個人服務(wù)器的資源使用情況,對應(yīng)的安裝指令如下:
sudo apt update
sudo apt install htop而本次htop輸出的指標(biāo)如下:
- 服務(wù)器為6核,對應(yīng)的CPU使用率分別是3.9、0、0、2.7、0.7、1.4,按照業(yè)界的通用標(biāo)準(zhǔn),當(dāng)前服務(wù)器各核心CPU使用率較低,但需結(jié)合系統(tǒng)負(fù)載綜合判斷
- Mem代表了內(nèi)存使用率,內(nèi)存一般情況下是用于存儲程序代碼和數(shù)據(jù),分為物理內(nèi)存和虛擬內(nèi)存,物理內(nèi)存顯示內(nèi)存接近8G僅用了1G不到,使用率不到80%,說明資源冗余
- Swp顯示交換空間即虛擬內(nèi)存的使用情況,可以看到也僅僅用了32M,并沒有大量的內(nèi)存數(shù)據(jù)被置換到交換空間,結(jié)合第2點來看,內(nèi)存資源充足
- Tasks顯示進(jìn)程數(shù)和線程數(shù)一共有35個進(jìn)程,這35個進(jìn)程對應(yīng)100個線程處理,Kthr顯示指標(biāo)為0說明有0個內(nèi)核線程,而Running為1說明有一個用戶進(jìn)程在運行
- 而系統(tǒng)平均負(fù)載近1分鐘為4.96,按照業(yè)界標(biāo)準(zhǔn)CPU核心數(shù)*0.75作為系統(tǒng)負(fù)載的運算閾值,如果超過這個值則說明系統(tǒng)處于高負(fù)載狀態(tài),很明顯我們的6核服務(wù)器系統(tǒng)負(fù)載偏高了
綜合來看,服務(wù)器系統(tǒng)負(fù)載偏高但各CPU核心使用率較低,結(jié)合內(nèi)存使用情況,問題可能出現(xiàn)在I/O資源等待上,此時我們就要從I/O資源角度進(jìn)一步排查問題:

我們從I/O資源排查入手,通過vmstat 1執(zhí)行每秒一次的監(jiān)控指標(biāo)輸出,以筆者的服務(wù)器為例,可以看到如下幾個指標(biāo):
- r:按照文檔解釋為The number of runnable processes (running or waiting for run time)即正在運行或等待運行的進(jìn)程數(shù),如果大于CPU核心數(shù)則說明CPU處于過載狀態(tài),而當(dāng)前服務(wù)器這個值為0,說明隊列處理狀態(tài)良好
- b: 按照文檔解釋為The number of processes blocked waiting for I/O to complete即等待I/O完成的進(jìn)程數(shù),從參數(shù)b可以看出有大量進(jìn)程等待I/O,說明當(dāng)前服務(wù)器存在I/O瓶頸。
- swpd: the amount of swap memory used即交換空間也就是虛擬內(nèi)存的使用,而當(dāng)前服務(wù)器已被使用30468說明已經(jīng)在使用交換空間,由此參數(shù)結(jié)合buff(緩存中尚未寫入磁盤的內(nèi)容)和cache(從磁盤加載出來的緩存數(shù)據(jù))來看,當(dāng)前內(nèi)存資源持續(xù)升高,存在讀寫虛擬內(nèi)存的情況,存在I/O性能瓶頸。
- 從bo來看有大量任務(wù)進(jìn)行每秒寫塊
- 針對CPU一個板塊輸出的us(用戶代碼執(zhí)行時間)、sy(內(nèi)核執(zhí)行時間)、id(空閑時間)、wa(等待I/O的時間),其中wa即等待I/O的時間持續(xù)處于一個高數(shù)值的狀態(tài),更進(jìn)一步明確CPU在空轉(zhuǎn),等待I/O完成,而I/O資源處于吃緊的狀態(tài)

考慮為I/O資源瓶頸,我們優(yōu)先從網(wǎng)絡(luò)I/O角度排查問題,這里筆者采用nload進(jìn)行網(wǎng)絡(luò)資源診斷,如果沒有下載的可以自行通過yum或者apt的方式自行下載,這里筆者也貼出ubuntu服務(wù)器的下載指令:
# ubuntu下載安裝nload
sudo apt install nload -y鍵入nload實時輸出網(wǎng)絡(luò)帶寬的使用情況,可以看到:
- 輸入流量(incoming)即下載流量,當(dāng)前下載速度約為1KB/s,占最大帶寬的20%左右,一般認(rèn)為只有當(dāng)網(wǎng)速接近最大帶寬時才說明帶寬使用率接近飽和
- 輸出流量(outgoing)即上傳流量,同理當(dāng)前也僅僅使用8%,也未達(dá)到飽和的閾值
所以I/O資源吃緊的問題原因并非網(wǎng)絡(luò)I/O,我們需要進(jìn)一步從服務(wù)器磁盤I/O角度進(jìn)一步排查:

所以從這些指標(biāo)來看,存在大量的線程在等待I/O資源的分配而進(jìn)入阻塞,所以筆者基于iostat -x 1使每一秒都輸出更詳細(xì)的信息,可以看到sdd盤對應(yīng)的磁盤忙碌百分比util基本跑滿100%,已基本接近飽和,此時基本是確認(rèn)有大量線程在進(jìn)行I/O讀寫任務(wù)了,且查看I/O讀寫指標(biāo):
- 每秒讀寫的吞吐量w/s為175
- 每秒寫入wkB/s的172704KB
- SSD盤util即I/O資源利用率為100%,已經(jīng)遠(yuǎn)超業(yè)界閾值60%,說明存在I/O性能瓶頸,需要補充說明的是當(dāng)CPU和I/O設(shè)備達(dá)到100%利用率時,都可能導(dǎo)致進(jìn)程阻塞,但I(xiàn)/O設(shè)備的處理機(jī)制與CPU調(diào)度不同,在高負(fù)載情況下更容易導(dǎo)致大量I/O請求排隊等待
- 寫請求的平均等待時間w_await為5151ms
換算下來172704KB/175每秒寫入的速率為987KB每秒,由此可確定因為磁盤性能讀寫性能瓶頸導(dǎo)致大量I/O讀寫任務(wù)阻塞,進(jìn)而導(dǎo)致服務(wù)器卡頓,用戶體驗差:

所以,對于系統(tǒng)延遲嚴(yán)重的情況,整體排查思路為:
- 通過top指令查看CPU使用率,若正常進(jìn)入步驟2
- 基于vmstat查看內(nèi)存使用率和I/O資源情況
- 基于nload查看網(wǎng)絡(luò)I/O使用情況
- 基于iostat查看網(wǎng)絡(luò)I/O和磁盤I/O情況最終確認(rèn)問題
本例子的最后筆者也給出本次故障問題的示例代碼:
/**
* 啟動磁盤I/O操作以模擬高I/O負(fù)載
* 通過創(chuàng)建多個I/O任務(wù)線程來模擬高磁盤I/O負(fù)載
*/
private static void startDiskIOOperations() {
log.info("開始高I/O磁盤操作...");
log.info("在另一個終端中運行 'iostat -x 1' 來監(jiān)控磁盤利用率。");
// 創(chuàng)建固定線程數(shù)的線程池
executor = Executors.newFixedThreadPool(NUM_THREADS);
// 提交多個任務(wù)以連續(xù)寫入磁盤
for (int i = 0; i < NUM_THREADS; i++) {
executor.submit(new IOTask(i));
}
log.info("磁盤I/O操作已啟動,使用 {} 個線程", NUM_THREADS);
}
/**
* 執(zhí)行連續(xù)寫入操作以模擬高I/O的任務(wù)
* 該類負(fù)責(zé)執(zhí)行磁盤I/O操作,通過不斷寫入和清空文件來模擬高I/O負(fù)載
*/
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);
// 連續(xù)將數(shù)據(jù)寫入文件并在每次寫入后清空文件
while (!Thread.currentThread().isInterrupted()) {
performDiskIOOperation(fos, taskId);
ThreadUtil.sleep(500);
}
} catch (IOException e) {
log.error("線程-{} 發(fā)生錯誤: {}", taskId, e.getMessage());
}
}
}2. 系統(tǒng)操作卡頓
第二個例子也同樣是用戶反饋系統(tǒng)操作卡頓感嚴(yán)重,整體點擊響應(yīng)非常慢,我們還是考慮資源吃緊,優(yōu)先使用top指令查看資源使用情況,從top指令來看:
- 輸出us查看各個核心的CPU使用率跑滿
- 系統(tǒng)平均負(fù)載基本超過70%(6*0.7)已經(jīng)超過4.2
這就是經(jīng)典的計算密集型任務(wù)跑滿所有線程的經(jīng)典例子

一般針對CPU跑滿的問題,筆者一般會通過mpstat -P ALL 1查看CPU時間片是否分配均衡,是否出現(xiàn)偏斜導(dǎo)致CPU過熱的情況,例如所有運算任務(wù)都往一個CPU核心上跑,經(jīng)過筆者每秒1次的輸出持續(xù)觀察來看,整體資源吃緊,但并沒有出現(xiàn)資源分配偏斜的情況,同時內(nèi)存資源使用率也不高,也沒有大量的iowait等待:

結(jié)合第一步top指令定位到的進(jìn)程是Java進(jìn)程,筆者索性通過Arthas直接定位故障代碼,首先通過thread鎖定問題線程,可以看到pool-前綴的線程基本都是跑滿單個CPU,所以我們就可以通過thread id查看線程的棧幀:

最終鎖定到了這段代碼段,即一個密集的循環(huán)運算的線程:

對應(yīng)的筆者也貼出故障代碼段示例,來總結(jié)一下系統(tǒng)使用卡頓的排查思路:
- 基本top查看用戶態(tài)和內(nèi)核態(tài)CPU使用率
- 用戶態(tài)使用率偏高,通過mpstat查看CPU使用是否偏斜,是否保證CPU親和力
- 如果CPU使用沒有出現(xiàn)偏斜,則直接通過問題定位到Java進(jìn)程,結(jié)合Arthas快速定位問題線程進(jìn)行診斷
/**
* 模擬CPU使用率過高的情況
* 通過創(chuàng)建多個CPU密集型任務(wù)線程來模擬高CPU使用率
*/
public static void startHighCPUUsage() {
log.info("開始模擬高CPU使用率...");
// 創(chuàng)建CPU密集型任務(wù)的線程池
ExecutorService cpuExecutor = Executors.newFixedThreadPool(NUM_THREADS);
// 提交多個CPU密集型任務(wù)
for (int i = 0; i < NUM_THREADS; i++) {
cpuExecutor.submit(new CPUIntensiveTask(i));
}
log.info("高CPU使用率模擬已啟動,使用 {} 個線程", NUM_THREADS);
}
/**
* CPU密集型任務(wù),用于模擬高CPU使用率
* 該類通過執(zhí)行復(fù)雜的數(shù)學(xué)計算來占用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密集型任務(wù)-{} 已啟動", taskId);
// 執(zhí)行CPU密集型計算以提高CPU使用率
while (!Thread.currentThread().isInterrupted()) {
// 執(zhí)行一些復(fù)雜的數(shù)學(xué)計算
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任務(wù)-{} 完成一輪計算,結(jié)果: {}", taskId, result);
}
log.info("CPU密集型任務(wù)-{} 已結(jié)束", taskId);
}
}3. 持續(xù)的偶發(fā)性系統(tǒng)卡頓問題排查
此類問題比較棘手,系統(tǒng)偶發(fā)卡頓意味著是瞬時、頻繁的資源吃緊,我們還是直接使用top指令無法明確立刻捕捉到進(jìn)程,可能剛剛一看到飆升的進(jìn)程就消失了。
同理使用mpstat、vmstat指令無法準(zhǔn)確定位到超短期飆升問題的進(jìn)程,而基于iostat也沒有看到明顯的I/O資源吃緊,所以我們可以采用perf指令解決問題,以筆者的Ubuntu服務(wù)器為例,對應(yīng)的安裝步驟:
# 安裝perf工具
sudo apt install linux-tools-generic
sudo apt install linux-tools-common在筆者完成安裝并啟動之后,系統(tǒng)拋出WARNING: perf not found for kernel xxxx的異常,對應(yīng)的解決方案是要主動安裝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完成上述安裝之后,我們就可以通過將頻率降低設(shè)置為99并將監(jiān)控結(jié)果導(dǎo)出到tmp目錄下的perf.data中:
sudo perf record -F 99 -a -g -o /tmp/perf.data sleep 10可能很多讀者好奇為什么需要將頻率設(shè)置為99Hz,這樣做的目的是為了避免與系統(tǒng)定時器中斷頻率(通常為100Hz)同步,從而避免鎖步采樣導(dǎo)致的偏差。
鎖步采樣是指采樣頻率與系統(tǒng)定時器中斷頻率相同或成倍數(shù)關(guān)系時,采樣點會固定在相同的時間位置上,導(dǎo)致采樣結(jié)果不能準(zhǔn)確反映系統(tǒng)整體的性能狀況。
使用99Hz這樣的素數(shù)頻率可以減少與系統(tǒng)周期性活動同步的概率,從而獲得更全面、更準(zhǔn)確的性能數(shù)據(jù)。
舉個簡單的例子,若我們試圖確定道路是否出現(xiàn)擁堵,且通過24h一次的抽檢查,那么當(dāng)前樣本就可能與交通保持一個平行的同步狀態(tài),例如:
- 交通車流情況在每天8點-12點擁堵,而我們的程序也是恰好在每天9點采集,那么它就會認(rèn)為交通情況異常擁堵
- 若每天14點進(jìn)行一次采集那么就避開了交通阻塞的高峰期則會得到一個相反的、也是不正確的結(jié)論
為了規(guī)避相同周期頻率導(dǎo)致的lockstep即鎖同步采樣,我們可以適當(dāng)降低頻率避免與交通周期時間同步,保證一天的數(shù)據(jù)能夠在一個周期內(nèi)被完整地采集到,而本例最好的做法就是將定時間隔改為23h,這樣一來每個23天的樣本周期就會得到一天中所有時間的數(shù)據(jù)就能做到全面地了解到交通情況:

等待片刻后perf指令就會將結(jié)果輸出到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進(jìn)程對應(yīng)線程CPU使用率飆升到86,此時我們就可以基于這條信息到指定的進(jìn)程上查看該線程是否存在密集的運算:

最后我們也給出本示例的問題代碼:
/**
* 模擬CPU瞬間飆高然后降低的情況
* 實現(xiàn)每10秒一次的CPU使用率飆高和降低循環(huán)(僅使用單核)
*/
public static void startCPUSpikeAndDrop() {
log.info("開始模擬CPU瞬間飆高然后降低...");
// 創(chuàng)建用于CPU飆高的線程池(僅使用單核)
ExecutorService spikeExecutor = Executors.newFixedThreadPool(1);
// 提交單個CPU密集型任務(wù)來制造飆高
spikeExecutor.submit(new CPUSpikeTask(0));
log.info("CPU瞬間飆高已啟動,使用 {} 個線程", 1);
// 每隔10秒切換CPU飆高狀態(tài),實現(xiàn)周期性飆高和降低
Thread spikeController = new Thread(() -> {
boolean isSpiking = true;
ExecutorService currentExecutor = spikeExecutor;
while (!Thread.currentThread().isInterrupted()) {
try {
// 等待10秒
Thread.sleep(10000);
if (isSpiking) {
// 停止當(dāng)前的CPU飆高任務(wù)
currentExecutor.shutdownNow();
log.info("CPU使用率已降低");
} else {
// 啟動新的CPU飆高任務(wù)
currentExecutor = Executors.newFixedThreadPool(1);
currentExecutor.submit(new CPUSpikeTask(0));
log.info("CPU使用率已飆高");
}
// 切換狀態(tài)
isSpiking = !isSpiking;
} catch (InterruptedException e) {
log.error("CPU飆高控制線程被中斷", e);
break;
}
}
});
spikeController.setDaemon(true);
spikeController.start();
}
/**
* CPU瞬間飆高任務(wù),用于模擬CPU瞬間飆高然后降低的情況
* 該類通過執(zhí)行密集的數(shù)學(xué)計算來模擬CPU使用率的瞬時飆高,并在指定時間后自動停止
*/
static class CPUSpikeTask implements Runnable {
private final int taskId;
public CPUSpikeTask(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
log.info("CPU瞬間飆高任務(wù)-{} 已啟動", taskId);
// 執(zhí)行空循環(huán)以提高CPU使用率
while (!Thread.currentThread().isInterrupted()) {
// 空循環(huán)消耗CPU
}
log.info("CPU瞬間飆高任務(wù)-{} 已結(jié)束", taskId);
}
}三、小結(jié)
本文針對應(yīng)用延遲、系統(tǒng)卡頓、偶發(fā)頻繁卡頓三種常見的系統(tǒng)故障給出通用普適的排查思路,整體來說此類問題歸根結(jié)底都是系統(tǒng)資源吃緊,需要找到飽和的資源結(jié)合代碼推測根源并制定修復(fù)策略,以本文為例,通用的排查思路都為:
- 基于top查看CPU、內(nèi)存、負(fù)載
- 若CPU未飽和則通過vmstat查看I/O資源使用情況
- 明確I/O瓶頸通過nload和iostat查詢是網(wǎng)絡(luò)I/O還是磁盤I/O
- 若上述排查都無果,且CPU負(fù)載偶發(fā)飆高,可通過perf并調(diào)整頻率監(jiān)控系統(tǒng)定位系統(tǒng)中異常運行的資源
- 結(jié)合上述推斷結(jié)果查看是否是異常消耗,如果是則優(yōu)化代碼,反之結(jié)合情況增加硬件資源
此外,對于內(nèi)存相關(guān)問題,還可以通過以下方式進(jìn)一步診斷:
- 使用ps命令查看進(jìn)程的內(nèi)存使用情況,特別關(guān)注RSS(常駐內(nèi)存)和VSZ(虛擬內(nèi)存)字段
- 使用pmap命令查看進(jìn)程的內(nèi)存映射情況,識別是否存在異常的內(nèi)存段
- 使用valgrind等工具檢測C/C++程序的內(nèi)存泄漏問題
- 對于Java應(yīng)用,可結(jié)合jstat、jmap等工具分析堆內(nèi)存使用情況



























