負載(Load)分析及問題排查

平常的工作中,在衡量服務器的性能時,經常會涉及到幾個指標,load、cpu、mem、qps、rt等。每個指標都有其獨特的意義,很多時候在線上出現問題時,往往會伴隨着某些指標的異常。大部分情況下,在問題發生之前,某些指標就會提前有異常顯示。

對於這些指標的理解和查看、異常解決等,是程序員們重要的必備技能。本文,主要來介紹一下一個比較重要的指標——機器負載(Load),主要涉及負載的定義、查看負載方式、負載飆高排查思路等。

什麼是負載

負載(load)是linux機器的一個重要指標,直觀了反應了機器當前的狀態。

來看下負載的定義是怎樣的:

In UNIX computing, the system load is a measure of the amount of computational work that a computer system performs. The load average represents the average system load over a period of time. It conventionally appears in the form of three numbers which represent the system load during the last one-, five-, and fifteen-minute periods.(wikipedia)

簡單解釋一下:在UNIX系統中,系統負載是對當前CPU工作量的度量,被定義爲特定時間間隔內運行隊列中的平均線程數。load average 表示機器一段時間內的平均load。這個值越低越好。負載過高會導致機器無法處理其他請求及操作,甚至導致死機。

Linux的負載高,主要是由於CPU使用、內存使用、IO消耗三部分構成。任意一項使用過多,都將導致服務器負載的急劇攀升。

複製代碼

       /proc/loadavg
              The  first  three fields in this file are load average figures giving the number of jobs in the run queue (state
              R) or waiting for disk I/O (state D) averaged over 1, 5, and 15 minutes.  They are the same as the load  average
              numbers  given  by  uptime(1) and other programs.  The fourth field consists of two numbers separated by a slash
              (/).  The first of these is the number of currently executing kernel scheduling entities  (processes,  threads);
              this  will  be  less  than  or  equal  to the number of CPUs.  The value after the slash is the number of kernel
              scheduling entities that currently exist on the system.  The fifth field is the PID of the process that was most
              recently created on the system.

複製代碼

這段話大意是說,loadavg文件中前三個字段是平均負載值,分別代表1、5和15分鐘的作業(job)數量的平均值,作業(job)包括運行隊列(state R)或者等待磁盤I/O(state D)兩種類型。這裏面有這麼幾層信息:

  1. /proc/loadavg中前三個數字分別表示load1、load5、load15的值。
  2. load值代表的是對應時間內的jobs的平均數量,比如load1就表示過去1分鐘內的jobs數量的平均值。job主要是一個shell概念,和進程組概念近似,這裏應該屬於用詞不當(後面會分析,準確的用詞應該是內核中的tasks或用戶空間中的threads概念)。
  3. 而且只包含state狀態爲R和D的兩種jobs,其他state狀態不包含在內。

查看機器負載。

在Linux機器上,有多個命令都可以查看機器的負載信息。其中包括uptimetopw等。

uptime命令

uptime命令能夠打印系統總共運行了多長時間和系統的平均負載。uptime命令可以顯示的信息顯示依次爲:現在時間、系統已經運行了多長時間、目前有多少登陸用戶、系統在過去的1分鐘、5分鐘和15分鐘內的平均負載。

➜  ~ uptime
13:29  up 23:41, 3 users, load averages: 1.74 1.87 1.97
複製代碼

這行信息的後半部分,顯示"load average",它的意思是"系統的平均負荷",裏面有三個數字,我們可以從中判斷系統負荷是大還是小。

1.74 1.87 1.97 這三個數字的意思分別是1分鐘、5分鐘、15分鐘內系統的平均負荷。我們一般表示爲load1、load5、load15。

w命令

w命令的主要功能其實是顯示目前登入系統的用戶信息。但是與who不同的是,w命令功能更加強大,w命令還可以顯示:當前時間,系統啓動到現在的時間,登錄用戶的數目,系統在最近1分鐘、5分鐘和15分鐘的平均負載。然後是每個用戶的各項數據,項目顯示順序如下:登錄帳號、終端名稱、遠 程主機名、登錄時間、空閒時間、JCPU、PCPU、當前正在運行進程的命令行。

➜  ~ w
14:08  up 23:41, 3 users, load averages: 1.74 1.87 1.97
USER     TTY      FROM              LOGIN@  IDLE WHAT
hollis   console  -                六14   23:40 -
hollis   s000     -                六14   20:24 -zsh
hollis   s001     -                六15       - w
複製代碼

從上面的w命令的結果可以看到,當前系統時間是14:08,系統啓動到現在經歷了23小時41分鐘,共有3個用戶登錄。系統在近1分鐘、5分鐘和15分鐘的平均負載分別是1.74 1.87 1.97。這和uptime得到的結果相同。 下面還打印了一些登錄的用戶的各項數據,不詳細介紹了。

top命令

top命令是Linux下常用的性能分析工具,能夠實時顯示系統中各個進程的資源佔用狀況,類似於Windows的任務管理器。

➜  ~ top
Processes: 244 total, 3 running, 9 stuck, 232 sleeping, 1484 threads                                                                                                                               14:16:01
Load Avg: 1.74, 1.87, 1.97  CPU usage: 8.0% user, 6.79% sys, 85.19% idle   SharedLibs: 116M resident, 16M data, 14M linkedit. MemRegions: 66523 total, 2152M resident, 50M private, 930M shared.
PhysMem: 7819M used (1692M wired), 370M unused. VM: 682G vsize, 533M framework vsize, 6402060(0) swapins, 7234356(0) swapouts. Networks: packets: 383006/251M in, 334448/60M out.
Disks: 1057821/38G read, 350852/40G written.

PID    COMMAND      %CPU TIME     #TH   #WQ  #PORT MEM    PURG   CMPRS  PGRP  PPID  STATE    BOOSTS          %CPU_ME %CPU_OTHRS UID  FAULTS    COW    MSGSENT   MSGRECV   SYSBSD    SYSMACH   CSW
30845  top          3.0  00:00.49 1/1   0    21    3632K  0B     0B     30845 1394  running  *0[1]           0.00000 0.00000    0    3283+     112    203556+   101770+   8212+     119901+   823+
30842  Google Chrom 0.0  00:47.39 17    0    155   130M   0B     0B     1146  1146  sleeping *0[1]           0.00000 0.00000    501  173746    2697   117678    37821     364228    444830    310043
複製代碼

上面的輸出結果中,Load Avg: 1.74, 1.87, 1.97顯示的就是負載信息。

機器正常負載範圍

對於機器的Load到底多少算正常的問題,一直都是很有爭議的,不同人有着不同的理解。對於單個CPU,有人認爲如果Load超過0.7就算是超出正常範圍了。也有人認爲只要不超過1都沒問題。也有人認爲,單個CPU的負載在2以下都可以接受。

爲什麼會有這麼多不同的理解呢,是因爲不同的機器除了CPU影響之外還有其他因素的影響,運行的程序、機器內存、甚至是機房溫度等都有可能有區別。

比如,有些機器用於定時執行大量的跑批任務,這個時間段內,Load可能會飆的比較高。而其他時間可能會比較低。那麼這段飆高時間我們要不要去排查問題呢?

我的建議是,最好根據自己機器的實際情況,建立一個指標的基線(如近一個月的平均值),只要日常的load在基線上下範圍內不太大都可以接收,如果差距太多可能就要人爲介入檢查了。

但是,總要有個建議的閾值吧,關於這個值。阮一峯在自己的博客中有過以下建議:

當系統負荷持續大於0.7,你必須開始調查了,問題出在哪裏,防止情況惡化。

當系統負荷持續大於1.0,你必須動手尋找解決辦法,把這個值降下來。

當系統負荷達到5.0,就表明你的系統有很嚴重的問題,長時間沒有響應,或者接近死機了。你不應該讓系統達到這個值。

以上指標都是基於單CPU的,但是現在很多電腦都是多核的。所以,對一般的系統來說,是根據cpu數量去判斷系統是否已經過載(Over Load)的。如果我們認爲0.7算是單核機器負載的安全線的話,那麼四核機器的負載最好保持在3(4*0.7 = 2.8)以下。

還有一點需要提一下,在Load Avg的指標中,有三個值,1分鐘系統負荷、5分鐘系統負荷,15分鐘系統負荷。我們在排查問題的時候也是可以參考這三個值的。

一般情況下,1分鐘系統負荷表示最近的暫時現象。15分鐘系統負荷表示是持續現象,並非暫時問題。如果load15較高,而load1較低,可以認爲情況有所好轉。反之,情況可能在惡化。

如何降低負載

導致負載高的原因可能很複雜,有可能是硬件問題也可能是軟件問題。

如果是硬件問題,那麼說明機器性能確實就不行了,那麼解決起來很簡單,直接換機器就可以了。

前面我們提過,CPU使用、內存使用、IO消耗都可能導致負載高。如果是軟件問題,有可能由於Java中的某些線程被長時間佔用、大量內存持續佔用等導致。建議從以下幾個方面排查代碼問題:

1、是否有內存泄露導致頻繁GC 2、是否有死鎖發生 3、是否有大字段的讀寫 4、會不會是數據庫操作導致的,排查SQL語句問題。5、死循環

這裏還有個建議,如果發現線上機器Load飆高,可以考慮先把堆棧內存dump下來後,進行重啓,暫時解決問題,然後再考慮回滾和排查問題。

Java Web應用Load飆高排查思路

1、使用uptime查看當前load,發現load飆高。

➜  ~ uptime
13:29  up 23:41, 3 users, load averages: 10 10 10
複製代碼

2、使用top命令,查看佔用CPU較高的進程ID。

➜  ~ top

PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
1893 admin     20   0 7127m 2.6g  38m S 181.7 32.6  10:20.26 java
複製代碼

發現PID爲1893的進程佔用CPU 181%。而且是一個Java進程,基本斷定是軟件問題。

3、使用 top命令,查看具體是哪個線程佔用率較高

➜  ~ top -Hp 1893
PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
4519 admin     20   0 7127m 2.6g  38m R 18.6 32.6   0:40.11 java
複製代碼

4、使用printf命令查看這個線程的16進制

➜  ~ printf %x 4519
11a7
複製代碼

5、使用jstack命令查看當前線程正在執行的方法。(Java命令學習系列(二)——Jstack)

➜  ~ jstack 1893 |grep -A 200 11a7
"thread-5" #500 daemon prio=10 os_prio=0 tid=0x00007f632314a800 nid=0x11a2 runnable [0x000000005442a000]
java.lang.Thread.State: RUNNABLE
at sun.misc.URLClassPath$Loader.findResource(URLClassPath.java:684)
at sun.misc.URLClassPath.findResource(URLClassPath.java:188)
at java.net.URLClassLoader$2.run(URLClassLoader.java:569)
at java.net.URLClassLoader$2.run(URLClassLoader.java:567)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findResource(URLClassLoader.java:566)
at org.hibernate.validator.internal.xml.ValidationXmlParser.getInputStreamForPath(ValidationXmlParser.java:248)
at com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)
複製代碼

從上面的線程的棧日誌中,可以發現,當前佔用CPU較高的線程正在執行我代碼的com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)類。那麼就可以去排查這個類是否用法有問題了。

分析Linux Load高時的一個誤區

  區分了R和D狀態線程對Linux load的影響,我們再來看一個絕大多數人都曾經歷的誤區。當linux系統load標高時,很多人都會去top一下,查看當前系統誰佔用的CPU Usage最高。

  •   這裏補充一個背景知識,只有當進程(線程)處於R狀態時,才耗費CPU Usage,其他狀態(包括D狀態)並不耗費CPU Usage。當load高主要是由D狀態線程數量過多導致的時候,此時從top中按CPU Usage的排名是不會發現任何線索的。
  •   即使當load高是由R狀態線程數量過多導致,如果運行top命令時導致load高的R狀態線程已經結束,此時也不會從按CPU Usage的排名的top輸出中發現線索的。默認情況下,top命令是3秒中刷新,只顯示3秒內的CPU Usage信息。


作者:Hollis_公衆號Hollis
鏈接:https://juejin.im/post/5b0262edf265da0b9b079fa7
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章