1 測(cè)試工具
ab (apache bench)是一個(gè)常用的HTTP服務(wù)性能測(cè)試工具
yum install -y httpd-tools
# 并發(fā)10個(gè)請(qǐng)求測(cè)試Nginx性能,總共測(cè)試100個(gè)請(qǐng)求
$ ab -c 10 -n 100 http://192.168.0.10:10000/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
...
Requests per second: 11.63 [#/sec] (mean)
Time per request: 859.942 [ms] (mean)
...
表示并發(fā)請(qǐng)求書(shū)為11.63,吞吐量不是很好,正常為2237左右
$ ab -c 5 -t 600 http://192.168.0.10:10000/
-c 并發(fā)請(qǐng)求數(shù)為5
-t 時(shí)間為10min
-n 一共100個(gè)請(qǐng)求
hping3
hping3 是一個(gè)可以構(gòu)造 TCP/IP 協(xié)議數(shù)據(jù)包的工具,可以對(duì)系統(tǒng)進(jìn)行安全審計(jì)、防火墻測(cè)試等。運(yùn)行 hping3 命令,來(lái)模擬 Nginx 的客戶端請(qǐng)求
# -S參數(shù)表示設(shè)置TCP協(xié)議的SYN(同步序列號(hào)),-p表示目的端口為80
# -i u100表示每隔100微秒發(fā)送一個(gè)網(wǎng)絡(luò)幀
# 注:如果你在實(shí)踐過(guò)程中現(xiàn)象不明顯,可以嘗試把100調(diào)小,比如調(diào)成10甚至1
$ hping3 -S -p 80 -i u100 192.168.0.30
雖然在運(yùn)行 hping3 命令時(shí),我就已經(jīng)告訴你,這是一個(gè) SYN FLOOD 攻擊,你肯定也會(huì)想到從網(wǎng)絡(luò)方面入手,來(lái)分析這個(gè)問(wèn)題。不過(guò),在實(shí)際的生產(chǎn)環(huán)境中,沒(méi)人直接告訴你原因。
tcpdump
# -i eth0 只抓取eth0網(wǎng)卡,-n不解析協(xié)議名和主機(jī)名
# tcp port 80表示只抓取tcp協(xié)議并且端口號(hào)為80的網(wǎng)絡(luò)幀
$ tcpdump -i eth0 -n tcp port 80
15:11:32.678966 IP 192.168.0.2.18238 > 192.168.0.30.80: Flags [S], seq 458303614, win 512, length 0
...
2 指標(biāo)
CPU高
3 工具
yum -y install perf sysstat dstat(perf pidstat)
top,htop
pidstat——專門(mén)分析每個(gè)進(jìn)程CPU使用情況的工具
pidstat -p $PID
perf——perf是linux2.6.31以后內(nèi)置的性能分析工具。它以性能事件采樣為基礎(chǔ),不僅可以分析系統(tǒng)的各種事件和內(nèi)核性能,還可以分析指定應(yīng)用程序的性能問(wèn)題。是內(nèi)置于linux內(nèi)核源碼數(shù)中的性能剖析工具,常用于性能瓶頸的查找和熱點(diǎn)代碼的定位
perf top主要是用于分析各個(gè)函數(shù)在某個(gè)性能事件上的熱度,能夠快速的定位熱點(diǎn)函數(shù),包括應(yīng)用程序函數(shù)
execsnoop 就是一個(gè)專為短時(shí)進(jìn)程設(shè)計(jì)的工具。它通過(guò) ftrace 實(shí)時(shí)監(jiān)控進(jìn)程的 exec() 行為,并輸出短時(shí)進(jìn)程的基本信息,包括進(jìn)程 PID、父進(jìn)程 PID、命令行參數(shù)以及執(zhí)行的結(jié)果
dstat 它吸收了 vmstat、iostat、ifstat 等幾種工具的優(yōu)點(diǎn),可以同時(shí)觀察系統(tǒng)的 CPU、磁盤(pán) I/O、網(wǎng)絡(luò)以及內(nèi)存使用情況
strace 正是最常用的跟蹤進(jìn)程系統(tǒng)調(diào)用的工具
pstree查找父進(jìn)程
sar 是一個(gè)系統(tǒng)活動(dòng)報(bào)告工具,既可以實(shí)時(shí)查看系統(tǒng)的當(dāng)前活動(dòng),又可以配置保存和報(bào)告歷史統(tǒng)計(jì)數(shù)據(jù)。
tcpdump 是一個(gè)常用的網(wǎng)絡(luò)抓包工具,常用來(lái)分析各種網(wǎng)絡(luò)問(wèn)題。
【yum -y install hping3 tcpdump】
4 命令
top
# 按下數(shù)字 1 切換到所有 CPU 的使用情況,觀察一會(huì)兒按 Ctrl+C 結(jié)束
$ top
top - 05:56:23 up 17 days, 16:45, 2 users, load average: 2.00, 1.68, 1.39
Tasks: 247 total, 1 running, 79 sleeping, 0 stopped, 115 zombie
%Cpu0 : 0.0 us, 0.7 sy, 0.0 ni, 38.9 id, 60.5 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.7 sy, 0.0 ni, 4.7 id, 94.6 wa, 0.0 hi, 0.0 si, 0.0 st
————————————
————————————
* us:用戶態(tài)CPU時(shí)間
* sy:內(nèi)核態(tài)CPU時(shí)間
* ni:低優(yōu)先級(jí)用戶態(tài)CPU時(shí)間
* id:空閑時(shí)間
* wa:等待I/O的CPU時(shí)間
* hi:處理硬中斷的CPU時(shí)間
* si:處理軟中斷的CPU時(shí)間
* st:當(dāng)系統(tǒng)運(yùn)行在虛擬機(jī)中的時(shí)候,被其他虛擬機(jī)占用的CPU時(shí)間
進(jìn)程狀態(tài):
R(Running) 表示進(jìn)程在CPU的就緒隊(duì)列中,正在運(yùn)行或者正在等待運(yùn)行
D(Disk Sleep) 不可中斷狀態(tài)睡眠,一般表示進(jìn)程正在和硬件交互,并且交互過(guò)程不允許被其他進(jìn)程或中斷打斷【系統(tǒng)硬件出現(xiàn)故障會(huì)導(dǎo)致此進(jìn)程增多,需關(guān)注是不是I/O等性能問(wèn)題】
Z(Zombie) 僵尸進(jìn)程 ,也就是進(jìn)程實(shí)際上已經(jīng)結(jié)束了,但是父進(jìn)程還沒(méi)有收回它的資源(比如進(jìn)程的描述符,PID等)
S(Interruptible Sleep) 也就是可中斷狀態(tài)失眠,表示進(jìn)程因?yàn)榈却硞€(gè)事件而被系統(tǒng)掛起。當(dāng)進(jìn)程等待事件發(fā)生時(shí),它會(huì)被喚醒并進(jìn)入R狀態(tài)。
I(Idel) 空閑狀態(tài),用在不可中斷睡眠的內(nèi)核線程上,硬件交互導(dǎo)致的不可中斷進(jìn)程用D表示,但對(duì)于某些內(nèi)核進(jìn)程來(lái)說(shuō),它們又可能實(shí)際上并沒(méi)有任何負(fù)載,用Idel正是為了區(qū)分這種情況,要注意,D狀態(tài)的進(jìn)程會(huì)導(dǎo)致平均負(fù)載升高,I狀態(tài)的進(jìn)程不會(huì)
T或者t,也就是Stopped或Traced的縮寫(xiě),表示進(jìn)程處于暫停或者跟蹤狀態(tài)
X(Dead)表示進(jìn)程已經(jīng)消亡,不會(huì)在top中查看到
進(jìn)程組表示一組相互關(guān)聯(lián)的進(jìn)程,比如每個(gè)子進(jìn)程都是父進(jìn)程所在組的成員;
會(huì)話是指共享同一個(gè)控制終端的一個(gè)或多個(gè)進(jìn)程組。
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4340 root 20 0 44676 4048 3432 R 0.3 0.0 0:00.05 top
4345 root 20 0 37280 33624 860 D 0.3 0.0 0:00.01 app
4344 root 20 0 37280 33624 860 D 0.3 0.4 0:00.01 app
1 root 20 0 160072 9416 6752 S 0.0 0.1 0:38.59 systemd
...
dstat
# 間隔1秒輸出10組數(shù)據(jù)
$ dstat 1 10
You did not select any stats, using -cdngy by default.
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai stl| read writ| recv send| in out | int csw
0 0 96 4 0|1219k 408k| 0 0 | 0 0 | 42 885
0 0 2 98 0| 34M 0 | 198B 790B| 0 0 | 42 138
0 0 0 100 0| 34M 0 | 66B 342B| 0 0 | 42 135
0 0 84 16 0|5633k 0 | 66B 342B| 0 0 | 52 177
0 3 39 58 0| 22M 0 | 66B 342B| 0 0 | 43 144
0 0 0 100 0| 34M 0 | 200B 450B| 0 0 | 46 147
0 0 2 98 0| 34M 0 | 66B 342B| 0 0 | 45 134
0 0 0 100 0| 34M 0 | 66B 342B| 0 0 | 39 131
0 0 83 17 0|5633k 0 | 66B 342B| 0 0 | 46 168
0 3 39 59 0| 22M 0 | 66B 342B| 0 0 | 37 134
從 dstat 的輸出,我們可以看到,每當(dāng) iowait 升高(wai)時(shí),磁盤(pán)的讀請(qǐng)求(read)都會(huì)很大。這說(shuō)明 iowait 的升高跟磁盤(pán)的讀請(qǐng)求有關(guān),很可能就是磁盤(pán)讀導(dǎo)致的。
pidstat
# 每隔1秒輸出一組數(shù)據(jù),共輸出5組
$ pidstat 1 5
15:56:02 UID PID %usr %system %guest %wait %CPU CPU Command
15:56:03 0 15006 0.00 0.99 0.00 0.00 0.99 1 dockerd
...
Average: UID PID %usr %system %guest %wait %CPU CPU Command
Average: 0 15006 0.00 0.99 0.00 0.00 0.99 - dockerd
-----------------
-----------------
用戶態(tài) CPU 使用率 (%usr)
內(nèi)核態(tài) CPU 使用率(%system)
運(yùn)行虛擬機(jī) CPU 使用率(%guest)
等待 CPU 使用率(%wait)
以及總的 CPU 使用率(%CPU)
# -d 展示 I/O 統(tǒng)計(jì)數(shù)據(jù),-p 指定進(jìn)程號(hào),間隔 1 秒輸出 3 組數(shù)據(jù)
$ pidstat -d -p 4344 1 3
06:38:50 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
06:38:51 0 4344 0.00 0.00 0.00 0 app
06:38:52 0 4344 0.00 0.00 0.00 0 app
06:38:53 0 4344 0.00 0.00 0.00 0 app
在這個(gè)輸出中, kB_rd 表示每秒讀的 KB 數(shù), kB_wr 表示每秒寫(xiě)的 KB 數(shù),iodelay 表示 I/O 的延遲(單位是時(shí)鐘周期)。它們都是 0,那就表示此時(shí)沒(méi)有任何的讀寫(xiě),說(shuō)明問(wèn)題不是 4344 進(jìn)程導(dǎo)致的。
pidstat -w -u 1 -w代表進(jìn)程切換指標(biāo),-u表示輸出cpu使用指標(biāo),每隔1s輸出1組數(shù)據(jù)
Average: UID PID %usr %system %guest %CPU CPU Command
vmstat
vmstat 5
重點(diǎn)關(guān)注:
io
- bi 從塊設(shè)備接收的塊(block/s)
- bo 發(fā)送給塊設(shè)備的塊(block/s).如果這個(gè)值長(zhǎng)期不為0,說(shuō)明內(nèi)存可能有問(wèn)題,因?yàn)闆](méi)有使用到緩存(當(dāng)然,不排除直接I/O的情況,但是一般很少有直接I/O的)
system
- in (interrupt) 每秒的中斷次數(shù),包括時(shí)鐘中斷,需要關(guān)注,這兩個(gè)值越大,內(nèi)核消耗CPU會(huì)越大
- cs (context switch) 進(jìn)程上下文切換次數(shù),需要關(guān)注
cpu
- us 用戶進(jìn)程占用CPU時(shí)間比例,需要關(guān)注us+sy是否已經(jīng)為100%
- sy 系統(tǒng)占用CPU時(shí)間比例
- id CPU空閑時(shí)間比
- wa IO等待時(shí)間比(IO等待高時(shí),可能是磁盤(pán)性能有問(wèn)題了)
- st steal time
proc
r (Running or Runnnable)是就緒隊(duì)列的長(zhǎng)度,也就是正在運(yùn)行和等待CPU的進(jìn)程數(shù)。對(duì)比cpu的個(gè)數(shù),如果等待進(jìn)程大于cpu個(gè)數(shù),需要注意
b (Blocked) 則是處于不可中斷睡眠狀態(tài)的進(jìn)程數(shù)


strace
strace 正是最常用的跟蹤進(jìn)程系統(tǒng)調(diào)用的工具。所以,我們從 pidstat 的輸出中拿到進(jìn)程的 PID 號(hào),比如 6082,然后在終端中運(yùn)行 strace 命令,并用 -p 參數(shù)指定 PID 號(hào)
$ strace -p 6082
strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted
這兒出現(xiàn)了一個(gè)奇怪的錯(cuò)誤,strace 命令居然失敗了,并且命令報(bào)出的錯(cuò)誤是沒(méi)有權(quán)限。按理來(lái)說(shuō),我們所有操作都已經(jīng)是以 root 用戶運(yùn)行了,為什么還會(huì)沒(méi)有權(quán)限呢?你也可以先想一下,碰到這種情況,你會(huì)怎么處理呢?一般遇到這種問(wèn)題時(shí),我會(huì)先檢查一下進(jìn)程的狀態(tài)是否正常。比如,繼續(xù)在終端中運(yùn)行 ps 命令,并使用 grep 找出剛才的 6082 號(hào)進(jìn)程:
果然,進(jìn)程 6082 已經(jīng)變成了 Z 狀態(tài),也就是僵尸進(jìn)程。僵尸進(jìn)程都是已經(jīng)退出的進(jìn)程,所以就沒(méi)法兒繼續(xù)分析它的系統(tǒng)調(diào)用。關(guān)于僵尸進(jìn)程的處理方法,我們一會(huì)兒再說(shuō),現(xiàn)在還是繼續(xù)分析 iowait 的問(wèn)題。到這一步,你應(yīng)該注意到了,系統(tǒng) iowait 的問(wèn)題還在繼續(xù),但是 top、pidstat 這類(lèi)工具已經(jīng)不能給出更多的信息了。這時(shí),我們就應(yīng)該求助那些基于事件記錄的動(dòng)態(tài)追蹤工具了。你可以用 perf top 看看有沒(méi)有新發(fā)現(xiàn)。再或者,可以像我一樣,在終端中運(yùn)行 perf record,持續(xù)一會(huì)兒(例如 15 秒),然后按 Ctrl+C 退出,再運(yùn)行 perf report 查看報(bào)告:
perf top
$ perf top
Samples: 833 of event 'cpu-clock', Event count (approx.): 97742399
Overhead Shared Object Symbol
7.28% perf [.] 0x00000000001f78a4
4.72% [kernel] [k] vsnprintf
4.32% [kernel] [k] module_get_kallsym
3.65% [kernel] [k] _raw_spin_unlock_irqrestore
...
類(lèi)似與top,它能夠?qū)崟r(shí)顯示占用CPU時(shí)鐘最多的函數(shù)或者指令,因此可以用來(lái)查找熱點(diǎn)函數(shù)
輸出結(jié)果中,第一行包含三個(gè)數(shù)據(jù),分別是采樣數(shù)(Samples)、事件類(lèi)型(event)和事件總數(shù)量(Event count)。
比如這個(gè)例子中,perf 總共采集了 833 個(gè) CPU 時(shí)鐘事件,而總事件數(shù)則為 97742399。
另外,采樣數(shù)需要我們特別注意。如果采樣數(shù)過(guò)少(比如只有十幾個(gè)),那下面的排序和百分比就沒(méi)什么實(shí)際參考價(jià)值了。
再往下看是一個(gè)表格式樣的數(shù)據(jù),每一行包含四列,分別是:
第一列 Overhead ,是該符號(hào)的性能事件在所有采樣中的比例,用百分比來(lái)表示。
第二列 Shared ,是該函數(shù)或指令所在的動(dòng)態(tài)共享對(duì)象(Dynamic Shared Object),如內(nèi)核、進(jìn)程名、動(dòng)態(tài)鏈接庫(kù)名、內(nèi)核模塊名等。第三列 Object ,是動(dòng)態(tài)共享對(duì)象的類(lèi)型。比如 [.] 表示用戶空間的可執(zhí)行程序、或者動(dòng)態(tài)鏈接庫(kù),而 [k] 則表示內(nèi)核空間。
最后一列 Symbol 是符號(hào)名,也就是函數(shù)名。
當(dāng)函數(shù)名未知時(shí),用十六進(jìn)制的地址來(lái)表示。還是以上面的輸出為例,我們可以看到,占用 CPU 時(shí)鐘最多的是 perf 工具自身,不過(guò)它的比例也只有 7.28%,說(shuō)明系統(tǒng)并沒(méi)有 CPU 性能問(wèn)題。 perf top 的使用你應(yīng)該很清楚了吧。
perf record和perf report
perf top 雖然實(shí)時(shí)展示了系統(tǒng)的性能信息,但它的缺點(diǎn)是并不保存數(shù)據(jù),也就無(wú)法用于離線或者后續(xù)的分析。而 perf record 則提供了保存數(shù)據(jù)的功能,保存后的數(shù)據(jù),需要你用 perf report 解析展示
$ perf record # 按Ctrl+C終止采樣
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.452 MB perf.data (6093 samples) ]
$ perf report # 展示類(lèi)似于perf top的報(bào)告
perf -g
# -g開(kāi)啟調(diào)用關(guān)系分析,-p指定php-fpm的進(jìn)程號(hào)21515
$ perf top -g -p 21515
按方向鍵切換到 php-fpm,再按下回車(chē)鍵展開(kāi) php-fpm 的調(diào)用關(guān)系,你會(huì)發(fā)現(xiàn),調(diào)用關(guān)系最終到了 sqrt 和 add_function??磥?lái),我們需要從這兩個(gè)函數(shù)入手了

5 場(chǎng)景
CPU使用率是最直觀和最常用的系統(tǒng)性能指標(biāo),更是我們?cè)谂挪樾阅軉?wèn)題時(shí),通常會(huì)關(guān)注的第一個(gè)指標(biāo)。
- 用戶CPU和Nice CPU高,說(shuō)明用戶態(tài)進(jìn)程占用了較多的CPU,所以應(yīng)該著重排查進(jìn)程的性能問(wèn)題
- 系統(tǒng)CPU高,說(shuō)明內(nèi)核態(tài)占用了較多的CPU,所以應(yīng)該著重排查內(nèi)核線程或者系統(tǒng)調(diào)用的性能問(wèn)題
- I/O等待CPU高,說(shuō)明等待I/O的時(shí)間比較長(zhǎng),所以應(yīng)該著重排查系統(tǒng)存儲(chǔ)是不是出現(xiàn)了I/O問(wèn)題
- 軟中斷和硬中斷高,說(shuō)明軟中斷和硬中斷的處理程序占用了較多的CPU,所以應(yīng)該著重排查內(nèi)核中的中斷服務(wù)程序。
6 CPU 高——無(wú)法找到高cpu進(jìn)程
并不是所有情況都可以用上面的方法排查
$ top
...
%Cpu(s): 80.8 us, 15.1 sy, 0.0 ni, 2.8 id, 0.0 wa, 0.0 hi, 1.3 si, 0.0 st
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
6882 root 20 0 8456 5052 3884 S 2.7 0.1 0:04.78 docker-containe
6947 systemd+ 20 0 33104 3716 2340 S 2.7 0.0 0:04.92 nginx
7494 daemon 20 0 336696 15012 7332 S 2.0 0.2 0:03.55 php-fpm
7495 daemon 20 0 336696 15160 7480 S 2.0 0.2 0:03.55 php-fpm
10547 daemon 20 0 336696 16200 8520 S 2.0 0.2 0:03.13 php-fpm
10155 daemon 20 0 336696 16200 8520 S 1.7 0.2 0:03.12 php-fpm
10552 daemon 20 0 336696 16200 8520 S 1.7 0.2 0:03.12 php-fpm
15006 root 20 0 1168608 66264 37536 S 1.0 0.8 9:39.51 dockerd
4323 root 20 0 0 0 0 I 0.3 0.0 0:00.87 kworker/u4:1
...
# 間隔1秒輸出一組數(shù)據(jù)(按Ctrl+C結(jié)束)
$ pidstat 1
...
04:36:24 UID PID %usr %system %guest %wait %CPU CPU Command
04:36:25 0 6882 1.00 3.00 0.00 0.00 4.00 0 docker-containe
04:36:25 101 6947 1.00 2.00 0.00 1.00 3.00 1 nginx
04:36:25 1 14834 1.00 1.00 0.00 1.00 2.00 0 php-fpm
04:36:25 1 14835 1.00 1.00 0.00 1.00 2.00 0 php-fpm
04:36:25 1 14845 0.00 2.00 0.00 2.00 2.00 1 php-fpm
04:36:25 1 14855 0.00 1.00 0.00 1.00 1.00 1 php-fpm
04:36:25 1 14857 1.00 2.00 0.00 1.00 3.00 0 php-fpm
04:36:25 0 15006 0.00 1.00 0.00 0.00 1.00 0 dockerd
04:36:25 0 15801 0.00 1.00 0.00 0.00 1.00 1 pidstat
04:36:25 1 17084 1.00 0.00 0.00 2.00 1.00 0 stress
04:36:25 0 31116 0.00 1.00 0.00 0.00 1.00 0 atopacctd
...
觀察top和pidstat得知,所有的進(jìn)程cpu使用率都不高,可是總體的cpu使用率卻為80.8 us,
%Cpu(s): 80.8 us, 15.1 sy, 0.0 ni,......
根本就找不到高cpu進(jìn)程,這可怎么辦?
后來(lái)我發(fā)現(xiàn),會(huì)出現(xiàn)這種情況,很可能是因?yàn)榍懊娴姆治雎┝艘恍╆P(guān)鍵信息。你可以先暫停一下,自己往上翻,重新操作檢查一遍?;蛘撸覀円黄鸱祷厝シ治?top 的輸出,看看能不能有新發(fā)現(xiàn)
再次查看top發(fā)現(xiàn),

有6個(gè)R狀態(tài)的進(jìn)程,查看卻是stress(測(cè)試)進(jìn)程
$ pidstat -p 24344(進(jìn)程id)
16:14:55 UID PID %usr %system %guest %wait %CPU CPU Command
# 從所有進(jìn)程中查找PID是24344的進(jìn)程
$ ps aux | grep 24344
root 9628 0.0 0.0 14856 1096 pts/0 S+ 16:15 0:00 grep --color=auto 24344
pidstat和ps居然沒(méi)有輸出。終于發(fā)現(xiàn),原來(lái)這個(gè)進(jìn)程已經(jīng)不存在了,所以pidstat和ps都沒(méi)有輸出,再次查看top,cpu還是很高,進(jìn)程還在,但進(jìn)程已經(jīng)更換了id號(hào)
進(jìn)程的id在變,在我看來(lái),要么是這些進(jìn)程在不停地重啟,要么就是全新的進(jìn)程,這無(wú)非也就兩個(gè)原因:第一個(gè)原因,進(jìn)程在不停地崩潰重啟,比如因?yàn)槎五e(cuò)誤、配置錯(cuò)誤等等,這時(shí),進(jìn)程在退出后可能又被監(jiān)控系統(tǒng)自動(dòng)重啟了。第二個(gè)原因,這些進(jìn)程都是短時(shí)進(jìn)程,也就是在其他應(yīng)用內(nèi)部通過(guò) exec 調(diào)用的外面命令。這些命令一般都只運(yùn)行很短的時(shí)間就會(huì)結(jié)束,你很難用 top 這種間隔時(shí)間比較長(zhǎng)的工具發(fā)現(xiàn)(上面的案例,我們碰巧發(fā)現(xiàn)了)。至于 stress,我們前面提到過(guò),它是一個(gè)常用的壓力測(cè)試工具。它的 PID 在不斷變化中,看起來(lái)像是被其他進(jìn)程調(diào)用的短時(shí)進(jìn)程。要想繼續(xù)分析下去,還得找到它們的父進(jìn)程。要怎么查找一個(gè)進(jìn)程的父進(jìn)程呢?沒(méi)錯(cuò),用 pstree 就可以用樹(shù)狀形式顯示所有進(jìn)程之間的關(guān)系:
$ pstree | grep stress
|-docker-containe-+-php-fpm-+-php-fpm---sh---stress
| |-3*[php-fpm---sh---stress---stress]
找到父進(jìn)程后,再繼續(xù)排查
用 execsnoop 監(jiān)控上述案例,就可以直接得到 stress 進(jìn)程的父進(jìn)程 PID 以及它的命令行參數(shù),并可以發(fā)現(xiàn)大量的 stress 進(jìn)程在不停啟動(dòng)
# 按 Ctrl+C 結(jié)束
$ execsnoop
PCOMM PID PPID RET ARGS
sh 30394 30393 0
stress 30396 30394 0 /usr/local/bin/stress -t 1 -d 1
sh 30398 30393 0
stress 30399 30398 0 /usr/local/bin/stress -t 1 -d 1
sh 30402 30400 0
stress 30403 30402 0 /usr/local/bin/stress -t 1 -d 1
sh 30405 30393 0
stress 30407 30405 0 /usr/local/bin/stress -t 1 -d 1
execsnoop 所用的 ftrace 是一種常用的動(dòng)態(tài)追蹤技術(shù),一般用于分析 Linux 內(nèi)核的運(yùn)行時(shí)行為
...
7 CPU高——IO高
top查看是否有IO高的情況
pidstat查看具體
# -d 展示 I/O 統(tǒng)計(jì)數(shù)據(jù),-p 指定進(jìn)程號(hào),間隔 1 秒輸出 3 組數(shù)據(jù)$ pidstat -d -p 4344 1 3
# 間隔 1 秒輸出多組數(shù)據(jù) (這里是 20 組)$ pidstat -d 1 20
$ pidstat -d 1 20
$ strace -p 6082
strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted
$ perf record -g
$ perf report

app 的確在通過(guò)系統(tǒng)調(diào)用 sys_read() 讀取數(shù)據(jù)。并且從 new_sync_read 和 blkdev_direct_IO 能看出,進(jìn)程正在對(duì)磁盤(pán)進(jìn)行直接讀,也就是繞過(guò)了系統(tǒng)緩存,每個(gè)讀請(qǐng)求都會(huì)從磁盤(pán)直接讀,這就可以解釋我們觀察到的 iowait 升高了
8 CPU高——僵尸進(jìn)程多
既然僵尸進(jìn)程是因?yàn)楦高M(jìn)程沒(méi)有回收子進(jìn)程的資源而出現(xiàn)的,那么,要解決掉它們,就要找到它們的根兒,也就是找出父進(jìn)程,然后在父進(jìn)程里解決。
父進(jìn)程的找法我們前面講過(guò),最簡(jiǎn)單的就是運(yùn)行 pstree 命令:
# -a 表示輸出命令行選項(xiàng)
# p表PID
# s表示指定進(jìn)程的父進(jìn)程
$ pstree -aps 3084
systemd,1
└─dockerd,15006 -H fd://
└─docker-containe,15024 --config /var/run/docker/containerd/containerd.toml
└─docker-containe,3991 -namespace moby -workdir...
└─app,4009
└─(app,3084)
9 CPU高——軟中斷
top
# top運(yùn)行后按數(shù)字1切換到顯示所有CPU
$ top
top - 10:50:58 up 1 days, 22:10, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 122 total, 1 running, 71 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni, 96.7 id, 0.0 wa, 0.0 hi, 3.3 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni, 95.6 id, 0.0 wa, 0.0 hi, 4.4 si, 0.0 st
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7 root 20 0 0 0 0 S 0.3 0.0 0:01.64 ksoftirqd/0
16 root 20 0 0 0 0 S 0.3 0.0 0:01.97 ksoftirqd/1
2663 root 20 0 923480 28292 13996 S 0.3 0.3 4:58.66 docker-containe
3699 root 20 0 0 0 0 I 0.3 0.0 0:00.13 kworker/u4:0
3708 root 20 0 44572 4176 3512 R 0.3 0.1 0:00.07 top
1 root 20 0 225384 9136 6724 S 0.0 0.1 0:23.25 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.03 kthreadd
...
仔細(xì)看 top 的輸出,兩個(gè) CPU 的使用率雖然分別只有 3.3% 和 4.4%,但都用在了軟中斷上;而從進(jìn)程列表上也可以看到,CPU 使用率最高的也是軟中斷進(jìn)程 ksoftirqd。看起來(lái),軟中斷有點(diǎn)可疑了。
查看中斷次數(shù)的變化速率
$ watch -d cat /proc/softirqs
CPU0 CPU1
HI: 0 0
TIMER: 1083906 2368646
NET_TX: 53 9
NET_RX: 1550643 1916776
BLOCK: 0 0
IRQ_POLL: 0 0
TASKLET: 333637 3930
SCHED: 963675 2293171
HRTIMER: 0 0
RCU: 1542111 1590625
通過(guò) /proc/softirqs 文件內(nèi)容的變化情況,你可以發(fā)現(xiàn), TIMER(定時(shí)中斷)、NET_RX(網(wǎng)絡(luò)接收)、SCHED(內(nèi)核調(diào)度)、RCU(RCU 鎖)等這幾個(gè)軟中斷都在不停變化。其中,NET_RX,也就是網(wǎng)絡(luò)數(shù)據(jù)包接收軟中斷的變化速率最快。而其他幾種類(lèi)型的軟中斷,是保證 Linux 調(diào)度、時(shí)鐘和臨界區(qū)保護(hù)這些正常工作所必需的,所以它們有一定的變化倒是正常的。那么接下來(lái),我們就從網(wǎng)絡(luò)接收的軟中斷著手,繼續(xù)分析。既然是網(wǎng)絡(luò)接收的軟中斷,第一步應(yīng)該就是觀察系統(tǒng)的網(wǎng)絡(luò)接收情況。這里你可能想起了很多網(wǎng)絡(luò)工具,不過(guò),我推薦今天的主人公工具 sar 。
sar 可以用來(lái)查看系統(tǒng)的網(wǎng)絡(luò)收發(fā)情況,還有一個(gè)好處是,不僅可以觀察網(wǎng)絡(luò)收發(fā)的吞吐量(BPS,每秒收發(fā)的字節(jié)數(shù)),還可以觀察網(wǎng)絡(luò)收發(fā)的 PPS,即每秒收發(fā)的網(wǎng)絡(luò)幀數(shù)。我們?cè)诘谝粋€(gè)終端中運(yùn)行 sar 命令,并添加 -n DEV 參數(shù)顯示網(wǎng)絡(luò)收發(fā)的報(bào)告
# -n DEV 表示顯示網(wǎng)絡(luò)收發(fā)的報(bào)告,間隔1秒輸出一組數(shù)據(jù)
$ sar -n DEV 1
15:03:46 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
15:03:47 eth0 12607.00 6304.00 664.86 358.11 0.00 0.00 0.00 0.01
15:03:47 docker0 6302.00 12604.00 270.79 664.66 0.00 0.00 0.00 0.00
15:03:47 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
15:03:47 veth9f6bbcd 6302.00 12604.00 356.95 664.66 0.00 0.00 0.00 0.05
第一列:表示報(bào)告的時(shí)間。第二列:IFACE 表示網(wǎng)卡。第三、四列:rxpck/s 和 txpck/s 分別表示每秒接收、發(fā)送的網(wǎng)絡(luò)幀數(shù),也就是 PPS。第五、六列:rxkB/s 和 txkB/s 分別表示每秒接收、發(fā)送的千字節(jié)數(shù),也就是 BPS。后面的其他參數(shù)基本接近 0,顯然跟今天的問(wèn)題沒(méi)有直接關(guān)系,你可以先忽略掉。
既然懷疑是網(wǎng)絡(luò)接收中斷的問(wèn)題,我們還是重點(diǎn)來(lái)看 eth0 :接收的 PPS 比較大,達(dá)到 12607,而接收的 BPS 卻很小,只有 664 KB。直觀來(lái)看網(wǎng)絡(luò)幀應(yīng)該都是比較小的,我們稍微計(jì)算一下,664*1024/12607 = 54 字節(jié),說(shuō)明平均每個(gè)網(wǎng)絡(luò)幀只有 54 字節(jié),這顯然是很小的網(wǎng)絡(luò)幀,也就是我們通常所說(shuō)的小包問(wèn)題。
使用 tcpdump 抓取 eth0 上的包就可以了。我們事先已經(jīng)知道, Nginx 監(jiān)聽(tīng)在 80 端口,它所提供的 HTTP 服務(wù)是基于 TCP 協(xié)議的,所以我們可以指定 TCP 協(xié)議和 80 端口精確抓包。
# -i eth0 只抓取eth0網(wǎng)卡,-n不解析協(xié)議名和主機(jī)名
# tcp port 80表示只抓取tcp協(xié)議并且端口號(hào)為80的網(wǎng)絡(luò)幀
$ tcpdump -i eth0 -n tcp port 80
15:11:32.678966 IP 192.168.0.2.18238 > 192.168.0.30.80: Flags [S], seq 458303614, win 512, length 0
...
從 tcpdump 的輸出中,你可以發(fā)現(xiàn)192.168.0.2.18238 > 192.168.0.30.80 ,表示網(wǎng)絡(luò)幀從 192.168.0.2 的 18238 端口發(fā)送到 192.168.0.30 的 80 端口,也就是從運(yùn)行 hping3 機(jī)器的 18238 端口發(fā)送網(wǎng)絡(luò)幀,目的為 Nginx 所在機(jī)器的 80 端口。Flags [S] 則表示這是一個(gè) SYN 包。再加上前面用 sar 發(fā)現(xiàn)的, PPS 超過(guò) 12000 的現(xiàn)象,現(xiàn)在我們可以確認(rèn),這就是從 192.168.0.2 這個(gè)地址發(fā)送過(guò)來(lái)的 SYN FLOOD 攻擊
SYN FLOOD 問(wèn)題最簡(jiǎn)單的解決方法,就是從交換機(jī)或者硬件防火墻中封掉來(lái)源 IP,這樣 SYN FLOOD 網(wǎng)絡(luò)幀就不會(huì)發(fā)送到服務(wù)器中
10 小結(jié)
1>
碰到CPU使用率升高的問(wèn)題,可以借助top pidstat等工具,確認(rèn)引發(fā)CPU性能問(wèn)題的來(lái)源,再使用perf等工具,排查引起性能問(wèn)題的具體函數(shù)。
2>
碰到常規(guī)問(wèn)題無(wú)法解釋的 CPU 使用率情況時(shí),首先要想到有可能是短時(shí)應(yīng)用導(dǎo)致的問(wèn)題,比如有可能是下面這兩種情況。第一,應(yīng)用里直接調(diào)用了其他二進(jìn)制程序,這些程序通常運(yùn)行時(shí)間比較短,通過(guò) top 等工具也不容易發(fā)現(xiàn)。第二,應(yīng)用本身在不停地崩潰重啟,而啟動(dòng)過(guò)程的資源初始化,很可能會(huì)占用相當(dāng)多的 CPU。對(duì)于這類(lèi)進(jìn)程,我們可以用 pstree 或者 execsnoop 找到它們的父進(jìn)程,再?gòu)母高M(jìn)程所在的應(yīng)用入手,排查問(wèn)題的根源
3>
iowait高不一定代表I/O有性能瓶頸,當(dāng)系統(tǒng)中只有I/O類(lèi)型的進(jìn)程在運(yùn)行時(shí),iowait也會(huì)很高,但實(shí)際上,磁盤(pán)的讀寫(xiě)遠(yuǎn)沒(méi)有達(dá)到性能瓶頸的程度。因此,碰到iowait升高時(shí),需要先用dstat,pidstat等工具,確認(rèn)是不是磁盤(pán)I/O問(wèn)題,然后再找出是哪些進(jìn)程導(dǎo)致了I/O。等待I/O進(jìn)程一般是不可中斷狀態(tài),所以用ps命令找到的D狀態(tài)進(jìn)程為 可以進(jìn)程,在本案例中,I/O操作后,進(jìn)程又變成了僵尸進(jìn)程,所以不能用strace直接分析這個(gè)進(jìn)程的系統(tǒng)調(diào)用。之后使用了perf工具,來(lái)分析系統(tǒng)的CPU時(shí)鐘事件,最終發(fā)現(xiàn)是直接I/O導(dǎo)致的問(wèn)題,這時(shí),再檢查源碼中對(duì)應(yīng)位置的問(wèn)題,就很輕松了。而僵尸進(jìn)程的問(wèn)題相對(duì)容易排查,使用pstree找出父進(jìn)程后,去查看父進(jìn)程的代碼,檢查wait()/waitpid()調(diào)用,或是SIGCHLD信號(hào)處理函數(shù)的注冊(cè)就行了
11 如何快速分析CPU瓶頸
cpu指標(biāo)
1> cpu使用率
* top 鍵盤(pán)1 查看每個(gè)邏輯CPU的狀況
* 顯示了系統(tǒng)總體的CPU和內(nèi)存使用情況,以及各個(gè)進(jìn)程的資源使用情況
* 需要關(guān)注的內(nèi)容
* us:用戶態(tài)CPU時(shí)間
* sy:內(nèi)核態(tài)CPU時(shí)間
* ni:低優(yōu)先級(jí)用戶態(tài)CPU時(shí)間
* id:空閑時(shí)間
* wa:等待I/O的CPU時(shí)間
* hi:處理硬中斷的CPU時(shí)間
* si:處理軟中斷的CPU時(shí)間
* st:當(dāng)系統(tǒng)運(yùn)行在虛擬機(jī)中的時(shí)候,被其他虛擬機(jī)占用的CPU時(shí)間
2> 平均負(fù)載
3>進(jìn)程上下文切換
無(wú)法獲取資源而導(dǎo)致的自愿上下文切換;被系統(tǒng)強(qiáng)制調(diào)度導(dǎo)致的非自愿上下文切換。上下文切換,本身是保證 Linux 正常運(yùn)行的一項(xiàng)核心功能。但過(guò)多的上下文切換,會(huì)將原本運(yùn)行進(jìn)程的 CPU 時(shí)間,消耗在寄存器、內(nèi)核棧以及虛擬內(nèi)存等數(shù)據(jù)的保存和恢復(fù)上,縮短進(jìn)程真正運(yùn)行的時(shí)間,成為性能瓶頸
cswch 表示每秒自愿上下文切換的次數(shù),是指進(jìn)程無(wú)法獲取所需資源,導(dǎo)致的上下文切換,比如說(shuō),I/O,內(nèi)存等系統(tǒng)資源不足時(shí),就會(huì)發(fā)生自愿上下文切換。
nvcswch 表示每秒非自愿上下文切換的次數(shù),則是指進(jìn)程由于時(shí)間片已到等原因,被系統(tǒng)強(qiáng)制調(diào)度,進(jìn)而發(fā)生的上下文切換。過(guò)多的線程在爭(zhēng)搶CPU
4>CPU緩存命中率
由于 CPU 發(fā)展的速度遠(yuǎn)快于內(nèi)存的發(fā)展,CPU 的處理速度就比內(nèi)存的訪問(wèn)速度快得多。這樣,CPU 在訪問(wèn)內(nèi)存的時(shí)候,免不了要等待內(nèi)存的響應(yīng)。為了協(xié)調(diào)這兩者巨大的性能差距,CPU 緩存(通常是多級(jí)緩存)就出現(xiàn)了。

就像上面這張圖顯示的,CPU 緩存的速度介于 CPU 和內(nèi)存之間,緩存的是熱點(diǎn)的內(nèi)存數(shù)據(jù)。根據(jù)不斷增長(zhǎng)的熱點(diǎn)數(shù)據(jù),這些緩存按照大小不同分為 L1、L2、L3 等三級(jí)緩存,其中 L1 和 L2 常用在單核中, L3 則用在多核中。從 L1 到 L3,三級(jí)緩存的大小依次增大,相應(yīng)的,性能依次降低(當(dāng)然比內(nèi)存還是好得多)。而它們的命中率,衡量的是 CPU 緩存的復(fù)用情況,命中率越高,則表示性能越好。
5>指標(biāo)篩選清單:

性能工具
1>
平均負(fù)載的案例。我們先用 uptime, 查看了系統(tǒng)的平均負(fù)載;而在平均負(fù)載升高后,又用 mpstat 和 pidstat ,分別觀察了每個(gè) CPU 和每個(gè)進(jìn)程 CPU 的使用情況,進(jìn)而找出了導(dǎo)致平均負(fù)載升高的進(jìn)程,也就是我們的壓測(cè)工具 stress
2>
上下文切換的案例。我們先用 vmstat ,查看了系統(tǒng)的上下文切換次數(shù)和中斷次數(shù);然后通過(guò) pidstat ,觀察了進(jìn)程的自愿上下文切換和非自愿上下文切換情況;最后通過(guò) pidstat ,觀察了線程的上下文切換情況,找出了上下文切換次數(shù)增多的根源,也就是我們的基準(zhǔn)測(cè)試工具 sysbench
3>
進(jìn)程 CPU 使用率升高的案例。我們先用 top ,查看了系統(tǒng)和進(jìn)程的 CPU 使用情況,發(fā)現(xiàn) CPU 使用率升高的進(jìn)程是 php-fpm;再用 perf top ,觀察 php-fpm 的調(diào)用鏈,最終找出 CPU 升高的根源,也就是庫(kù)函數(shù) sqrt()
4>
系統(tǒng)的 CPU 使用率升高的案例。我們先用 top 觀察到了系統(tǒng) CPU 升高,但通過(guò) top 和 pidstat ,卻找不出高 CPU 使用率的進(jìn)程。于是,我們重新審視 top 的輸出,又從 CPU 使用率不高但處于 Running 狀態(tài)的進(jìn)程入手,找出了可疑之處,最終通過(guò) perf record 和 perf report ,發(fā)現(xiàn)原來(lái)是短時(shí)進(jìn)程在搗鬼
另外,對(duì)于短時(shí)進(jìn)程,我還介紹了一個(gè)專門(mén)的工具 execsnoop,它可以實(shí)時(shí)監(jiān)控進(jìn)程調(diào)用的外部命令
5>
不可中斷進(jìn)程和僵尸進(jìn)程的案例。我們先用 top 觀察到了 iowait 升高的問(wèn)題,并發(fā)現(xiàn)了大量的不可中斷進(jìn)程和僵尸進(jìn)程;接著我們用 dstat 發(fā)現(xiàn)是這是由磁盤(pán)讀導(dǎo)致的,于是又通過(guò) pidstat 找出了相關(guān)的進(jìn)程。但我們用 strace 查看進(jìn)程系統(tǒng)調(diào)用卻失敗了,最終還是用 perf 分析進(jìn)程調(diào)用鏈,才發(fā)現(xiàn)根源在于磁盤(pán)直接 I/O
6>
軟中斷的案例。我們通過(guò) top 觀察到,系統(tǒng)的軟中斷 CPU 使用率升高;接著查看 /proc/softirqs, 找到了幾種變化速率較快的軟中斷;然后通過(guò) sar 命令,發(fā)現(xiàn)是網(wǎng)絡(luò)小包的問(wèn)題,最后再用 tcpdump ,找出網(wǎng)絡(luò)幀的類(lèi)型和來(lái)源,確定是一個(gè) SYN FLOOD 攻擊導(dǎo)致的
活學(xué)活用,把性能指標(biāo)和性能工具聯(lián)系起來(lái)
第一個(gè)維度,從 CPU 的性能指標(biāo)出發(fā)。也就是說(shuō),當(dāng)你要查看某個(gè)性能指標(biāo)時(shí),要清楚知道哪些工具可以做到

第二個(gè)維度,從工具出發(fā)。也就是當(dāng)你已經(jīng)安裝了某個(gè)工具后,要知道這個(gè)工具能提供哪些指標(biāo)

如何迅速分析 CPU 的性能瓶頸
想弄清楚性能指標(biāo)的關(guān)聯(lián)性,就要通曉每種性能指標(biāo)的工作原理
為了縮小排查范圍,我通常會(huì)先運(yùn)行幾個(gè)支持指標(biāo)較多的工具,如 top、vmstat 和 pidstat

通過(guò)這張圖你可以發(fā)現(xiàn),這三個(gè)命令,幾乎包含了所有重要的 CPU 性能指標(biāo),比如:從 top 的輸出可以得到各種 CPU 使用率以及僵尸進(jìn)程和平均負(fù)載等信息。從 vmstat 的輸出可以得到上下文切換次數(shù)、中斷次數(shù)、運(yùn)行狀態(tài)和不可中斷狀態(tài)的進(jìn)程數(shù)。從 pidstat 的輸出可以得到進(jìn)程的用戶 CPU 使用率、系統(tǒng) CPU 使用率、以及自愿上下文切換和非自愿上下文切換情況。
這三個(gè)工具輸出的很多指標(biāo)是相互關(guān)聯(lián)的,所以,我也用虛線表示了它們的關(guān)聯(lián)關(guān)系,舉幾個(gè)例子你可能會(huì)更容易理解
第一個(gè)例子,pidstat 輸出的進(jìn)程用戶 CPU 使用率升高,會(huì)導(dǎo)致 top 輸出的用戶 CPU 使用率升高。所以,當(dāng)發(fā)現(xiàn) top 輸出的用戶 CPU 使用率有問(wèn)題時(shí),可以跟 pidstat 的輸出做對(duì)比,觀察是否是某個(gè)進(jìn)程導(dǎo)致的問(wèn)題。而找出導(dǎo)致性能問(wèn)題的進(jìn)程后,就要用進(jìn)程分析工具來(lái)分析進(jìn)程的行為,比如使用 strace 分析系統(tǒng)調(diào)用情況,以及使用 perf 分析調(diào)用鏈中各級(jí)函數(shù)的執(zhí)行情況。
第二個(gè)例子,top 輸出的平均負(fù)載升高,可以跟 vmstat 輸出的運(yùn)行狀態(tài)和不可中斷狀態(tài)的進(jìn)程數(shù)做對(duì)比,觀察是哪種進(jìn)程導(dǎo)致的負(fù)載升高。如果是不可中斷進(jìn)程數(shù)增多了,那么就需要做 I/O 的分析,也就是用 dstat 或 sar 等工具,進(jìn)一步分析 I/O 的情況。如果是運(yùn)行狀態(tài)進(jìn)程數(shù)增多了,那就需要回到 top 和 pidstat,找出這些處于運(yùn)行狀態(tài)的到底是什么進(jìn)程,然后再用進(jìn)程分析工具,做進(jìn)一步分析。
最后一個(gè)例子,當(dāng)發(fā)現(xiàn) top 輸出的軟中斷 CPU 使用率升高時(shí),可以查看 /proc/softirqs 文件中各種類(lèi)型軟中斷的變化情況,確定到底是哪種軟中斷出的問(wèn)題。比如,發(fā)現(xiàn)是網(wǎng)絡(luò)接收中斷導(dǎo)致的問(wèn)題,那就可以繼續(xù)用網(wǎng)絡(luò)分析工具 sar 和 tcpdump 來(lái)分析。
12 CPU優(yōu)化思路
優(yōu)化前三問(wèn):
- 首先,既然要做性能優(yōu)化,那要怎么判斷它是不是有效呢?特別是優(yōu)化后,到底能提升多少性能呢?
- 第二,性能問(wèn)題通常不是獨(dú)立的,如果有多個(gè)性能問(wèn)題同時(shí)發(fā)生,你應(yīng)該先優(yōu)化哪一個(gè)呢?
- 第三,提升性能的方法并不是唯一的,當(dāng)有多種方法可以選擇時(shí),你會(huì)選用哪一種呢?是不是總選那個(gè)最大程度提升性能的方法就行了呢?
性能評(píng)估“三步走”:
- 確定性能的量化指標(biāo)
不要局限在單一維度的指標(biāo)上,你至少要從應(yīng)用程序和系統(tǒng)資源這兩個(gè)維度,分別選擇不同的指標(biāo)。
比如,以 Web 應(yīng)用為例:
應(yīng)用程序的維度,我們可以用吞吐量和請(qǐng)求延遲來(lái)評(píng)估應(yīng)用程序的性能。
系統(tǒng)資源的維度,我們可以用 CPU 使用率來(lái)評(píng)估系統(tǒng)的 CPU 使用情況
我們可以選擇 ab 等工具,測(cè)試 Web 應(yīng)用的并發(fā)請(qǐng)求數(shù)和響應(yīng)延遲。而測(cè)試的同時(shí),還可以用 vmstat、pidstat 等性能工具,觀察系統(tǒng)和進(jìn)程的 CPU 使用率。這樣,我們就同時(shí)獲得了應(yīng)用程序和系統(tǒng)資源這兩個(gè)維度的指標(biāo)數(shù)值。
注意:
第一,要避免性能測(cè)試工具干擾應(yīng)用程序的性能。通常,對(duì) Web 應(yīng)用來(lái)說(shuō),性能測(cè)試工具跟目標(biāo)應(yīng)用程序要在不同的機(jī)器上運(yùn)行。比如,在之前的 Nginx 案例中,我每次都會(huì)強(qiáng)調(diào)要用兩臺(tái)虛擬機(jī),其中一臺(tái)運(yùn)行 Nginx 服務(wù),而另一臺(tái)運(yùn)行模擬客戶端的工具,就是為了避免這個(gè)影響。
第二,避免外部環(huán)境的變化影響性能指標(biāo)的評(píng)估。這要求優(yōu)化前、后的應(yīng)用程序,都運(yùn)行在相同配置的機(jī)器上,并且它們的外部依賴也要完全一致。比如還是拿 Nginx 來(lái)說(shuō),就可以運(yùn)行在同一臺(tái)機(jī)器上,并用相同參數(shù)的客戶端工具來(lái)進(jìn)行性能測(cè)試。
- 測(cè)試優(yōu)化前的性能指標(biāo)
- 測(cè)試優(yōu)化后的性能指標(biāo)。
多個(gè)性能問(wèn)題存在如何選擇
在性能測(cè)試的領(lǐng)域,流傳很廣的一個(gè)說(shuō)法是“二八原則”,也就是說(shuō) 80% 的問(wèn)題都是由 20% 的代碼導(dǎo)致的。只要找出這 20% 的位置,你就可以優(yōu)化 80% 的性能。所以,我想表達(dá)的是,并不是所有的性能問(wèn)題都值得優(yōu)化
應(yīng)用程序優(yōu)化:排除不必要工作,只留核心邏輯
1.減少循環(huán)次數(shù) 減少遞歸 減少動(dòng)態(tài)沒(méi)錯(cuò)分配
2.編譯器優(yōu)化
3.算法優(yōu)化
4.異步處理
5.多線程代替多進(jìn)程
6.緩存
系統(tǒng)優(yōu)化:
利用CPU緩存本地性,加速緩存訪問(wèn);控制進(jìn)程的cpu使用情況,減少程序的處理速度
1.CPU綁定
2.CPU獨(dú)占
3.優(yōu)先級(jí)調(diào)整
4.為進(jìn)程設(shè)置資源限制
5.NUMA優(yōu)化
6.中斷負(fù)載均衡
很重要的一點(diǎn):切記過(guò)早優(yōu)化
參考連接: http://www.brendangregg.com/linuxperf.html
https://time.geekbang.org/column/intro/140