CPU高排查

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ù)
image.png

image.png

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ù)入手了
image.png
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),


image.png

有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
image.png

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)了。


image.png
就像上面這張圖顯示的,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)篩選清單:


image.png
性能工具

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í),要清楚知道哪些工具可以做到
image.png
第二個(gè)維度,從工具出發(fā)。也就是當(dāng)你已經(jīng)安裝了某個(gè)工具后,要知道這個(gè)工具能提供哪些指標(biāo)
image.png
如何迅速分析 CPU 的性能瓶頸

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


image.png

通過(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容