如何理解系統平均負載值(一)
1. 引
你們好,可愛的小伙伴們_。
每當我們發現系統變慢時,通常做的第一件事,就是執行top或者uptime命令,來了解系統的負載情況。比如下面這樣,我在命令行里輸入了uptime命令,系統也隨即給出了結果。
[root@localhost ~]# uptime
17:27:47 up 33 min, 2 users, load average: 0.63, 0.83, 0.88
但,我想問的是,你真的知道這里每列輸出的含義嗎?
我相信你對前面的幾列比較熟悉,他們分別是當前時間,系統運行時間以及正在登陸的用戶數
17:27:47 #系統當前時間
up 33 min #系統運行時間
2 users #正在登陸的用戶數
而最后的三個數字呢,依次則是過去1分鍾,5分鍾,15分鍾的平均負載值(Load Average)
2. 什么是平均負載值?
平均負載值:
- 這個詞對於很多人來說,可能既熟悉又陌生,我們每天的工作中,也都會提到這個詞,但你真正理解它背后的含義嗎?
- 我猜一定有人會說,平均負載不就是單位時間內的CPU使用率嗎?上面的0.63,就代表CPU使用率是63%。
- 其實並不是如此,如果同學們方便的話,可以通過執行man uptime,來了解平均負載的詳細解釋。
- 簡單講:平均負載就是指單位時間內,系統處於可運行狀態和不可中斷狀態的平均進程數,也就是平均活躍進程數。它和CPU使用率並沒有直接關系。
2.1 什么叫可運行狀態的進程?
可運行狀態的進程:指的是正在使用CPU或者正在等待CPU的進程,也就是我們常用ps命令看到的,處於R狀態(Running 或 Runnable)的進程
[root@localhost ~]# ps aux | grep dd
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1160 103 0.1 106212 1724 pts/1 R+ 17:50 0:03 dd if=/dev/zero of=/tmp/test bs=1M count=1000
參數備注說明:
- [x] USER:該進程屬於的用戶。
- [x] PID:該進程的進程號。
- [x] %CPU:該進程使用掉的CPU資源百分比。
- [x] %MEM:該進程所占用的物理內存百分比。
- [x] VSZ:該進程使用掉的虛擬內存量(單位Kbytes)
- [x] RSS:該進程占用的固定的內存量(單位Kbytes)
- [x] TTY:該進程是在哪個終端機上面運作的,若與終端機無關,則顯示“?”,另外,tty1-tty6是本機上面的登入者進程,若為pts/0等,則表示為由網絡連接進主機的進程。
- [x] STAT:該進程目前的狀態,主要的狀態包括如下幾種。
- R:正在運行,或者是可以運行。
- S:正在終端睡眠中,可以由某些信號喚醒。
- D:不可中斷睡眠。
- T:正在偵測或者是停止了。
- Z:已經終止,但是其父進程無法正常終止它,從而變成zombie(僵屍)進程的狀態
- +:前台進程。
- l:多線程進程。
- N:低優先級進程。
- <:高優先級進程。
- s:進程領導者。
- L:已將頁面鎖定到內存中。
- [x] START:該進程被觸發啟動的時間
- [x] TIME:該進程實際使用CPU運作的時間
- [x] COMMAND:該進程的實際命令。
2.2 什么叫不可中斷狀態的進程?
不可中斷狀態的進程:
- 指的是正處於內核態關鍵流程中的進程,並且這些流程是不可打斷的。
- 例如:等待硬件設備的I/O響應,也就是我們在ps命令中看到的D狀態(Uninterruptible Sleep,也叫做Disk Sleep)的進程
比如,當一個進程向磁盤讀寫數據時,為了保證數據的一致性,在得到磁盤回復前,它是不能被其他進程或者中斷打斷的,這個時候的進程就處於不可中斷狀態。如果此時的進程被打斷了,就容易出現磁盤數據與進程數據不一致的問題。 所以,不可中斷狀態實際上是系統對進程和硬件設備的一種保護機制。
2.3 什么叫平均活躍進程數
平均活躍進程數:
- 直觀的理解就是單位時間內的活躍進程數,但實際上是活躍進程數的指數衰減平均值。
- 這個指數衰減平均值的詳細含義不用計較,這只是系統的一種更快速的計算方式,你把它直接當成活躍進程數的平均值也沒問題。
既然,平均的是活躍進程數,那么最理想的,就是每個CPU上都剛好運行着一個進程,這樣每個CPU都得到了充分利用。比如當平均負載為2時,意味着什么呢?
- 在只有2個CPU核心的系統上,意味着所有的CPU都剛好被完全占用。
- 在4個CPU的系統上,意味着CPU有50%的空閑
- 而在只有1個CPU核心的系統中,則意味着有一半的進程競爭不到CPU
3. 平均負載值為多少時合理?
說完了什么是平均負載值,現在我們再回到最開始的例子,不知道同學們能否判斷出,在uptime命令的結果里,那三個時間段的平均負載數,多大的時候就說明系統負載高?或者是多小的時候就說明系統負載很低呢?
我們知道,平均負載最理想的情況是等於CPU個數。所以,在評判平均負載時,首先你要知道系統有幾個CPU,這可以通過top命令或者從文件/proc/cpuinfo中讀取,比如:
[root@localhost ~]# cat /proc/cpuinfo
processor : 0 #CPU的線程ID(邏輯CPU個數)
vendor_id : GenuineIntel
cpu family : 6
model : 94
model name : Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
stepping : 3
cpu MHz : 4007.997
cache size : 8192 KB
physical id : 0 #CPU的物理ID(物理CPU個數)
siblings : 2
core id : 0
cpu cores : 2 #單個物理CPU的總核心數
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 22
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf unfair_spinlock pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch ida arat epb xsaveopt pln pts dts fsgsbase bmi1 hle avx2 smep bmi2 invpcid rtm
bogomips : 8015.99
clflush size : 64
cache_alignment : 64
address sizes : 42 bits physical, 48 bits virtual
power management:
[root@localhost ~]# grep "processor" /proc/cpuinfo
processor : 0
processor : 1
processor : 2
processor : 3
[root@localhost ~]# grep "processor" /proc/cpuinfo | wc -l #該服務器的CPU一共有4個線程
4
[root@localhost ~]# egrep "physical id|cpu cores" /proc/cpuinfo
physical id : 0 #在物理ID為0的CPU上有兩個CPU核心
cpu cores : 2
physical id : 0
cpu cores : 2
physical id : 1 #在物理ID為1的CPU上有兩個CPU核心
cpu cores : 2
physical id : 1
cpu cores : 2
[root@localhost ~]# grep "physical id" /proc/cpuinfo | uniq | wc -l #該服務器一共有兩個物理CPU
2
[root@localhost ~]# grep "processor" /proc/cpuinfo | wc -l #該服務器一共有8個邏輯CPU
8
有了CPU的核心數,我們就可以判斷出,當平均負載值比CPU個數還大的時候,系統已經出現了過載。
不過,新的問題又來了。我們在例子中可以看到,平均負載有三個數值,到底該參考哪個呢?
實際上,都要看。三個不同時間間隔的平均值,其實給我們提供了,分析系統負載趨勢的數據來源,讓我們能更全面,更立體地理解目前的負載情況。
- 如果1分鍾,5分鍾,15分鍾的三個值基本相同,或者相差不大,那就說明系統負載很平穩
- 但如果1分鍾的值遠小於15分鍾的值,就說明系統最近1分鍾的負載在減小,而過去15分鍾內卻有很大的負載。
- 反過來,如果1分鍾的值遠大於15分鍾的值,就說明最近1分鍾的負載在增加,這種增加有可能只是臨時性的,也有可能還會持續增加下去,所以就需要持續觀察。一旦1分鍾的平均負載接近或超過了CPU的個數,就意味着系統正在發生過載的問題,這時就得分析調查是哪里導致的問題,並要想辦法優化了。
這里我們再舉個例子,假設我們在一個單CPU系統上看到平均負載為1.73,0.60,7.98,那么說明在過去的1分鍾內,系統有73%的超載,而在15分鍾內,有698%的超載,從整體趨勢來看,系統的負載在降低。
那么,在實際生產環境中,平均負載多高時,需要我們重點關注呢?
在我看來,當平均負載高於邏輯CPU數量的70%的時候,你就應該分析排查負載高的問題了。一旦負載過高,就可能導致進程響應變慢,進而影響服務的正常功能。
但是,70%這個數字並不是絕對的,最推薦的方法,還是把系統的平均負載監控起來,然后根據更多的歷史數據,判斷負載的變化趨勢。當發現負載有明顯升高趨勢時,比如說負載翻倍了,你再去做分析和調查。
4. 平均負載值與CPU使用率
現實工作中,我們經常容易把平均負載值和CPU使用率混淆,所以在這里,我也做一個區分。
可能你會疑惑,既然平均負載代表的是活躍進程數,那平均負載高了,不就意味着CPU使用率高嗎?
我們還是要回到平均負載的含義上來,平均負載是指單位時間內,處於可運行狀態和不可中斷狀態的進程數。所以,它不僅包括了正在使用CPU的進程,還包括等待CPU和等待I/O的進程。
而CPU使用率,是單位時間內CPU繁忙情況的統計,跟平均負載並不一定完全對應。比如:
- CPU密集型進程,使用大量CPU會導致平均負載升高,此時兩者是一致的。
- I/O密集型進程,等待I/O也會導致平均負載升高,但CPU使用率不一定很高。
- 大量等待CPU的進程調度也會導致平均負載升高,此時的CPU使用率也會比較高。
5. 平均負載案例分析
下面,我們以三個示例分別來看這三種情況,並用iostat,mpstat,pidstat等工具,找出平均負載升高的根源。
機器配置:2CPU,8GB內存
預先安裝stress和sysstat包
#系統環境示例
[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[root@localhost ~]# uname -r
3.10.0-862.3.3.el7.x86_64
[root@localhost ~]# yum -y install epel-release
[root@localhost ~]# yum -y install stress sysstat
#更新sysstat版本(CentOS默認sysstat版本過低,不支持查看%wait)
[root@localhost ~]# ls
anaconda-ks.cfg sysstat-12.1.5-1.x86_64.rpm
[root@localhost ~]# yum -y localinstall sysstat-12.1.5-1.x86_64.rpm
[root@localhost ~]# pidstat -V
sysstat 版本 12.1.5
(C) Sebastien Godard (sysstat <at> orange.fr)
5.1 工具介紹
stress:Linux系統壓力測試工具,這里我們用作異常進程模擬平均負載升高的場景
sysstat:
- 包含了常用的Linux性能工具,用來監控和分析系統的性能。
- 我們的案例會用到這個包的兩個命令mpstat和pidstat
- mpstat:一個常用的多核CPU性能分析工具,用來實時查看每個CPU的性能指標,以及所有CPU的平均指標
- pidstat:一個常用的進程性能分析工具,用來實時查看進程的CPU,內存,I/O以及上下文切換等性能指標。
5.2 案例分析
我們以下每個場景都要開三個終端,同時登陸到一台Linux機器中
下面所有命令,都是默認以root用戶運行。所以,如果你是普通用戶登陸的系統,一定要運行sudo su root命令切換到root用戶。
[root@localhost ~]# uptime
22:46:07 up 26 min, 2 users, load average: 0.00, 0.06, 0.11
5.2.1 場景一:CPU密集型進程
首先,我們在第一個終端運行stress命令,模擬一個CPU使用率100%的場景:
[root@localhost ~]# stress --cpu 1 --timeout 600
然后,在第二個終端運行uptime查看平均負載的變化情況:
#watch [參數] <command> 間歇執行命令
#-d 參數表示高亮顯示變化的區域
[root@localhost ~]# watch -d uptime
20:49:05 up 53 min, 4 users, load average: 0.81, 0.57, 0.33
最后,在第三個終端運行mpstat查看CPU使用率的變化情況:
[root@localhost ~]# mpstat -P ALL 5
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
20時55分31秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
20時55分36秒 all 49.95 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 49.85
20時55分36秒 0 6.80 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 93.00
20時55分36秒 1 93.01 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 6.79
從終端二中可以看到,1分鍾的平均負載會慢慢增加到趨近1.00,而從終端三中還可以看到,正好兩個CPU的使用率之和趨近100%,但它的iowait只有0.這說明,平均負載的升高正是由於CPU使用率的升高
5.2.2 場景二:I/O密集型進程
首先,還是運行stress命令,但是這次模擬I/O壓力,即不停地執行sync:
但是,由於stress命令是通過刷新緩沖區內存到磁盤的方式來提高I/O壓力;
可是,新安裝的虛擬機的緩沖區並沒有那么大,因此無法產生大的I/O壓力;
所以,在這里我們利用stress的下一代stress-ng命令來實現模擬。
[root@localhost ~]# yum -y install stress-ng
[root@localhost ~]# stress-ng -i 1 --hdd 1 --timeout 6000
stress-ng: info: [49531] dispatching hogs: 1 hdd, 1 io
還是在第二個終端運行uptime查看平均負載的變化:
[root@localhost ~]# watch -d uptime
20:59:37 up 1:03, 4 users, load average: 2.74, 1.55, 0.97
然后在第三個終端運行mpstat查看CPU使用率的變化情況:
21時09分13秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
21時09分18秒 all 0.31 0.00 26.97 37.46 0.00 0.94 0.00 0.00 0.00 34.31
21時09分18秒 0 0.00 0.00 15.52 60.34 0.00 1.72 0.00 0.00 0.00 22.41
21時09分18秒 1 0.61 0.00 37.83 15.75 0.00 0.20 0.00 0.00 0.00 45.60
從這里可以看到,1分鍾的平均負載慢慢增加到了2.74,其中兩個CPU的使用率之和為68%,但是iowait高達76%,再加上系統調用增加的消耗為53%。
那么,到底是哪個進程,導致的iowait這么高呢?我們用pidstat來查詢:
#間隔5秒輸出1組數據,-d 查看進程指標
[root@localhost ~]# pidstat -d 5 1
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
21時16分26秒 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
21時16分31秒 0 47719 0.00 0.00 0.00 311 kworker/u256:2
21時16分31秒 0 49532 0.00 1003121.76 486218.76 35 stress-ng-hdd
21時16分31秒 0 49533 0.00 0.00 0.00 62 stress-ng-io
平均時間: UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
平均時間: 0 47719 0.00 0.00 0.00 311 kworker/u256:2
平均時間: 0 49532 0.00 1003121.76 486218.76 35 stress-ng-hdd
平均時間: 0 49533 0.00 0.00 0.00 62 stress-ng-io
#間隔5秒輸出1組數據,-u 查看CPU指標
[root@localhost ~]# pidstat -u 5 1
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
21時17分18秒 UID PID %usr %system %guest %wait %CPU CPU Command
21時17分23秒 0 14 0.00 0.20 0.00 0.60 0.20 1 ksoftirqd/1
21時17分23秒 0 48943 0.00 9.40 0.00 0.60 9.40 0 kworker/u256:0
21時17分23秒 0 49532 0.80 49.20 0.00 0.20 50.00 0 stress-ng-hdd
21時17分23秒 0 49533 0.00 1.60 0.00 1.80 1.60 0 stress-ng-io
21時17分23秒 0 49538 0.00 2.40 0.00 1.00 2.40 0 kworker/0:7
21時17分23秒 0 49560 0.00 1.00 0.00 1.00 1.00 1 kworker/1:8
21時17分23秒 0 50294 0.00 0.20 0.00 0.00 0.20 1 pidstat
平均時間: UID PID %usr %system %guest %wait %CPU CPU Command
平均時間: 0 14 0.00 0.20 0.00 0.60 0.20 - ksoftirqd/1
平均時間: 0 48943 0.00 9.40 0.00 0.60 9.40 - kworker/u256:0
平均時間: 0 49532 0.80 49.20 0.00 0.20 50.00 - stress-ng-hdd
平均時間: 0 49533 0.00 1.60 0.00 1.80 1.60 - stress-ng-io
平均時間: 0 49538 0.00 2.40 0.00 1.00 2.40 - kworker/0:7
平均時間: 0 49560 0.00 1.00 0.00 1.00 1.00 - kworker/1:8
平均時間: 0 50294 0.00 0.20 0.00 0.00 0.20 - pidstat
可以發現,還是stress-ng進程導致的
5.2.3 場景三:大量進程的場景
當系統中運行進程超出CPU運行能力時,就會出現等待CPU的進程
比如,我們還是使用stress,但是這次模擬的是8個進程:
[root@localhost ~]# stress -c 8 --timeout 6000
stress: info: [60980] dispatching hogs: 8 cpu, 0 io, 0 vm, 0 hdd
由於系統只有兩個CPU,明顯比8個進程要少的多,因此,系統的CPU處於嚴重過載狀態
[root@localhost ~]# uptime
21:26:49 up 1:31, 4 users, load average: 7.44, 4.37, 2.79
接着,再運行pidstat來看一下進程的情況:
[root@localhost ~]# pidstat -u 5 1
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
21時27分27秒 UID PID %usr %system %guest %wait %CPU CPU Command
21時27分32秒 0 60981 24.95 0.00 0.00 75.05 24.95 0 stress
21時27分32秒 0 60982 24.95 0.00 0.00 74.65 24.95 1 stress
21時27分32秒 0 60983 24.75 0.00 0.00 75.05 24.75 1 stress
21時27分32秒 0 60984 25.15 0.00 0.00 75.45 25.15 1 stress
21時27分32秒 0 60985 24.95 0.00 0.00 75.05 24.95 0 stress
21時27分32秒 0 60986 25.15 0.00 0.00 74.65 25.15 0 stress
21時27分32秒 0 60987 24.95 0.00 0.00 74.85 24.95 1 stress
21時27分32秒 0 60988 24.75 0.00 0.00 75.25 24.75 0 stress
21時27分32秒 0 60993 0.00 0.20 0.00 1.20 0.20 1 kworker/1:0
21時27分32秒 0 60998 0.00 0.20 0.00 0.00 0.20 1 pidstat
平均時間: UID PID %usr %system %guest %wait %CPU CPU Command
平均時間: 0 60981 24.95 0.00 0.00 75.05 24.95 - stress
平均時間: 0 60982 24.95 0.00 0.00 74.65 24.95 - stress
平均時間: 0 60983 24.75 0.00 0.00 75.05 24.75 - stress
平均時間: 0 60984 25.15 0.00 0.00 75.45 25.15 - stress
平均時間: 0 60985 24.95 0.00 0.00 75.05 24.95 - stress
平均時間: 0 60986 25.15 0.00 0.00 74.65 25.15 - stress
平均時間: 0 60987 24.95 0.00 0.00 74.85 24.95 - stress
平均時間: 0 60988 24.75 0.00 0.00 75.25 24.75 - stress
平均時間: 0 60993 0.00 0.20 0.00 1.20 0.20 - kworker/1:0
平均時間: 0 60998 0.00 0.20 0.00 0.00 0.20 - pidstat
可以看出,8個進程在爭搶兩個CPU,每個進程等待CPU的時間(也就是代碼塊中的%wait列)高達75%。這些超出CPU計算能力的進程,最終導致CPU過載。
6.小結
平均負載值提供了一個快速查看系統整體性能的手段,反映了整體的負載情況。但,只看平均負載本身,我們並不能直接發現,到底是哪里出現了瓶頸。所以,在理解平均負載時,也要注意:
- 平均負載高有可能是CPU密集型進程導致的
- 平均負載高並不一定代表CPU使用率高,還有可能是I/O更繁忙了
- 當發現負載高的時候,你可以使用mpstat,pidstat等工具,輔助分析負載的來源