久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

linux中如何排查CPU與Load異常問題

共計(jì) 6113 個(gè)字符,預(yù)計(jì)需要花費(fèi) 16 分鐘才能閱讀完成。

這篇文章主要介紹了 linux 中如何排查 CPU 與 Load 異常問題,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓丸趣 TV 小編帶著大家一起了解一下。

一、top 命令

既然說(shuō)了 cpu 和 load,那總需要監(jiān)控吧,沒有監(jiān)控就不知道 cpu 和 load,后面的一切也就無(wú)從談起了。

top 命令是最常見的查看 cpu 和 load 的命令,拿我自己虛擬機(jī)上裝的 ubuntu 系統(tǒng)執(zhí)行一下 top 命令(默認(rèn) 3 秒刷 1 次,- d 可指定刷新時(shí)間):

做了一張表格比較詳細(xì)地解釋了每一部分的含義,其中重要屬性做了標(biāo)紅加粗:

內(nèi)存與 SWAP 輸出格式是一樣的,因此放在了一起寫。

二、cpu 如何計(jì)算

當(dāng)我們執(zhí)行 top 命令的時(shí)候,看到里面的值(主要是 cpu 和 load)是一直在變的,因此有必要簡(jiǎn)單了解一下 Linux 系統(tǒng)中 cpu 的計(jì)算方式。

cpu 分為系統(tǒng) cpu 和進(jìn)程、線程 cpu,系統(tǒng) cpu 的統(tǒng)計(jì)值位于 /proc/stat 下(以下的截圖未截全):

cpu、cpu0 后面的這些數(shù)字都和前面的 us、sy、ni 這些對(duì)應(yīng),具體哪個(gè)對(duì)應(yīng)哪個(gè)值不重要,感興趣的可以網(wǎng)上查一下文檔。

進(jìn)程 cpu 的統(tǒng)計(jì)值位于 /proc/{pid}/stat 下:

線程 cpu 的統(tǒng)計(jì)值位于 /proc/{pid}/task/{threadId}/stat 下:

這里面的所有值都是從系統(tǒng)啟動(dòng)時(shí)間到當(dāng)前時(shí)間的一個(gè)值。因此,對(duì)于 cpu 的計(jì)算的做法是,采樣兩個(gè)足夠短的時(shí)間 t1、t2:

  將 t1 的所有 cpu 使用情況求和,得到 s1;

  將 t2 的所有 cpu 使用情況求和,得到 s2;

 s2 – s1 得到這個(gè)時(shí)間間隔內(nèi)的所有時(shí)間 totalCpuTime;

  第一次的空閑 idle1 – 第二次的空閑 idle2,獲取采樣時(shí)間內(nèi)的空閑時(shí)間;

 cpu 使用率 = 100 * (totalCpuTime – idle) / totalCpuTime。

其他時(shí)間例如 us、sy、ni 都是類似的計(jì)算方式,總結(jié)起來(lái)說(shuō),cpu 這個(gè)值反應(yīng)的是某個(gè)采樣時(shí)間內(nèi)的 cpu 使用情況。因此有時(shí)候 cpu 很高,但是打印線程堆棧出來(lái)發(fā)現(xiàn)高 cpu 的線程在查詢數(shù)據(jù)庫(kù)等待中,不要覺得奇怪,因?yàn)?cpu 統(tǒng)計(jì)的是采樣時(shí)間內(nèi)的數(shù)據(jù)。

假設(shè) top 觀察某段時(shí)間用戶空間 cpu 一直很高,那么意味著這段時(shí)間用戶的程序一直在占據(jù)著 cpu 做事情。

三、對(duì) load 的理解

關(guān)于 load 的含義,其實(shí)有些文章把它跟行車過(guò)橋聯(lián)系在一起是比較恰當(dāng)和好理解的:

一個(gè)單核的處理器可以形象得比喻成一條單車道,車輛依次行駛在這條單車道上,前車駛過(guò)之后后車才可以行駛。

如果前面沒有車輛,那么你順利通過(guò);如果車輛眾多,那么你需要等待前車通過(guò)之后才可以通過(guò)。

因此,需要些特定的代號(hào)表示目前的車流情況,例如:

  等于 0.00,表示目前橋面上沒有任何的車流。實(shí)際上這種情況 0.00 和 1.00 之間是相同的,總而言之很通暢,過(guò)往的車輛可以絲毫不用等待的通過(guò);

  等于 1.00,表示剛好是在這座橋的承受范圍內(nèi)。這種情況不算糟糕,只是車流會(huì)有些堵,不過(guò)這種情況可能會(huì)造成交通越來(lái)越慢;

  大于 1.00,那么說(shuō)明這座橋已經(jīng)超出負(fù)荷,交通嚴(yán)重的擁堵。那么情況有多糟糕? 例如 2.00 的情況說(shuō)明車流已經(jīng)超出了橋所能承受的一倍,那么將有多余過(guò)橋一倍的車輛正在焦急的等待。

但是比喻終歸是比喻,從比喻中我們了解了,load 表示的是系統(tǒng)的一個(gè)能力,但是我們卻不知道什么樣的任務(wù)會(huì)被歸到 load 的計(jì)算中。關(guān)于具體怎么樣的任務(wù)會(huì)被歸到 load 的計(jì)算中,可以使用 man uptime 命令看一下 Linux 對(duì)于 load 的解釋:

大致意思就是說(shuō),系統(tǒng) load 是處于運(yùn)行狀態(tài)或者不可中斷狀態(tài)的進(jìn)程的平均數(shù)(標(biāo)紅部分表示被算入 load 的內(nèi)容)。一個(gè)處于運(yùn)行狀態(tài)的進(jìn)程表示正在使用 cpu 或者等待使用 cpu,一個(gè)不可中斷狀態(tài)的進(jìn)程表示正在等待 IO,例如磁盤 IO。load 的平均值通過(guò) 3 個(gè)時(shí)間間隔來(lái)展示,就是我們看到的 1 分鐘、5 分鐘、15 分鐘,load 值和 cpu 核數(shù)有關(guān),單核 cpu 的 load= 1 表示系統(tǒng)一直處在負(fù)載狀態(tài),但是 4 核 cpu 的 load= 1 表示系統(tǒng)有 75% 的空閑。

特別注意,load 指的是所有核的平均值,這和 cpu 的值是有區(qū)別的。

還有一個(gè)重要的點(diǎn)是,查了資料發(fā)現(xiàn),雖然上面一直強(qiáng)調(diào)的是 進(jìn)程,但是進(jìn)程中的線程數(shù)也是會(huì)被當(dāng)作不同的進(jìn)程來(lái)計(jì)算的,假如一個(gè)進(jìn)程產(chǎn)生 1000 個(gè)線程同時(shí)運(yùn)行,那運(yùn)行隊(duì)列的長(zhǎng)度就是 1000,load average 就是 1000。

四、請(qǐng)求數(shù)和 load 的關(guān)系

之前我自己一直有個(gè)誤區(qū):當(dāng)成千上萬(wàn)的請(qǐng)求過(guò)來(lái),且在排隊(duì)的時(shí)候,后面的請(qǐng)求得不到處理,load 值必然會(huì)升高。認(rèn)真思考之后,這個(gè)觀點(diǎn)可真是大錯(cuò)特錯(cuò),因此特別作為一段寫一下,和大家分享。

以 Redis 為例,我們都知道 Redis 是單線程模型的,這意味著同一時(shí)間可以有無(wú)數(shù)個(gè)請(qǐng)求過(guò)來(lái),但是同一時(shí)間只有一個(gè)命令會(huì)被處理。

圖片來(lái)源:https://www.processon.com/view/5c2ddab0e4b0fa03ce89d14f

單獨(dú)的一條線程接到就緒的命令之后,會(huì)將命令轉(zhuǎn)給事件分發(fā)器,事件分發(fā)器根據(jù)命令的類型執(zhí)行對(duì)應(yīng)的命令處理邏輯。由于只有一條線程,只要后面排隊(duì)的命令足夠多到讓這條線程一個(gè)接一個(gè)不停地處理命令,那么 load 表現(xiàn)就等于 1。

整個(gè)過(guò)程中,回看 load 這個(gè)值,它和請(qǐng)求數(shù)沒有任何關(guān)系,真正和 load 相關(guān)的是工作線程數(shù)量,main 線程是工作線程、Timer 是工作線程、GC 線程也是工作線程,load 是以線程 / 進(jìn)程作為統(tǒng)計(jì)指標(biāo),無(wú)論請(qǐng)求數(shù)是多少,最終都需要線程去處理,而工作線程的處理性能直接決定了最終的 load 值。

舉個(gè)例子,假設(shè)一個(gè)服務(wù)中有一個(gè)線程池,線程池中線程數(shù)量固定為 64:

  正常來(lái)說(shuō)一個(gè)任務(wù)執(zhí)行時(shí)間為 10ms,線程拿到任務(wù) 10ms 處理完,很快回歸線程池等待下一個(gè)任務(wù)到來(lái),自然很少有處于運(yùn)行狀態(tài)或者等待 IO 的線程,從一個(gè)統(tǒng)計(jì)周期來(lái)看 load 表現(xiàn)為很低;

  某段時(shí)間由于系統(tǒng)問題,一個(gè)任務(wù) 10s 都處理不完,相當(dāng)于線程一直在處理任務(wù),在 load 的統(tǒng)計(jì)周期里面就體現(xiàn)出的值 =64(不考慮這 64 條線程外的場(chǎng)景)。

因此,總而言之,搞清楚 load 值和請(qǐng)求數(shù)、線程數(shù)的關(guān)系非常重要,想清楚這些才能正確地進(jìn)行下一步的工作。

五、load 高、cpu 高的問題排查思路

首先拋出一個(gè)觀點(diǎn):cpu 高不是問題,由 cpu 高引起的 load 高才是問題,load 是判斷系統(tǒng)能力指標(biāo)的依據(jù)。

為什么這么說(shuō)呢,以單核 cpu 為例,當(dāng)我們?nèi)粘?cpu 在 20%、30% 的時(shí)候其實(shí)對(duì) cpu 資源是浪費(fèi)的,這意味著絕大多數(shù)時(shí)候 cpu 并沒有在做事,理論上來(lái)說(shuō)一個(gè)系統(tǒng)極限 cpu 利用率可以達(dá)到 100%,這意味著 cpu 完全被利用起來(lái)了處理計(jì)算密集型任務(wù),例如 for 循環(huán)、md5 加密、new 對(duì)象等等。但是實(shí)際不可能出現(xiàn)這種情況,因?yàn)閼?yīng)用程序中不消耗 cpu 的 IO 不存在是幾乎不可能的,例如讀取數(shù)據(jù)庫(kù)或者讀取文件,因此 cpu 不是越高越好,通常 75% 是一個(gè)需要引起警戒的經(jīng)驗(yàn)值。

注意前面提到的是 引起警戒,意味著 cpu 高不一定是問題,但是需要去看一下,尤其是日常的時(shí)候,因?yàn)橥ǔH粘A髁坎淮螅琧pu 是不可能打到這么高的。如果只是普通的代碼中確實(shí)在處理正常業(yè)務(wù)那沒問題,如果代碼里面出現(xiàn)了死循環(huán)(例如 JDK1.7 中經(jīng)典的 HashMap 擴(kuò)容引發(fā)的死循環(huán)問題),那么幾條線程一直占著 cpu,最后就會(huì)造成 load 的增高。

在一個(gè) Java 應(yīng)用中,排查 cpu 高的思路通常比較簡(jiǎn)單,有比較固定的做法:

 ps -ef | grep java,查詢 Java 應(yīng)用的進(jìn)程 pid;

 top -H -p pid,查詢占用 cpu 最高的線程 pid;

  將 10 進(jìn)制的線程 pid 轉(zhuǎn)成 16 進(jìn)制的線程 pid,例如 2000=0x7d0;

 jstack 進(jìn)程 pid | grep -A 20 0x7d0,查找 nid 匹配的線程,查看堆棧,定位引起高 cpu 的原因。

網(wǎng)上有很多文章寫到這里就停了,實(shí)踐過(guò)程中并不是這樣。因?yàn)?cpu 是時(shí)間段內(nèi)的統(tǒng)計(jì)值、jstack 是一個(gè)瞬時(shí)堆棧只記錄瞬時(shí)狀態(tài),兩個(gè)根本不是一個(gè)維度的事,因此完全有可能從打印出來(lái)的堆棧行號(hào)中看到代碼停留在以下地方:

  不消耗 cpu 的網(wǎng)絡(luò) IO;

 for (int i = 0, size = list.size(); i size; i++) {…};

  調(diào)用 native 方法。

如果完全按照上面那一套步驟做的話碰到這種情況就傻眼了,冥思苦想半天卻不得其解,根本不明白為什么這種代碼會(huì)導(dǎo)致高 cpu。針對(duì)可能出現(xiàn)的這種情況,實(shí)際排查問題的時(shí)候 jstack 建議打印 5 次至少 3 次,根據(jù)多次的堆棧內(nèi)容,再結(jié)合相關(guān)代碼段進(jìn)行分析,定位高 cpu 出現(xiàn)的原因,高 cpu 可能是代碼段中某個(gè) bug 導(dǎo)致的而不是堆棧打印出來(lái)的那幾行導(dǎo)致的。

另外,cpu 高的情況還有一種可能的原因,假如一個(gè) 4 核 cpu 的服務(wù)器我們看到總的 cpu 達(dá)到了 100%+,按 1 之后觀察每個(gè) cpu 的 us,只有一個(gè)達(dá)到了 90%+,其他都在 1% 左右(下圖只是演示 top 按 1 之后的效果并非真實(shí)場(chǎng)景):

這種情況下可以重點(diǎn)考慮是不是頻繁 FullGC 引起的。因?yàn)槲覀冎?FullGC 的時(shí)候會(huì)有 Stop The World 這個(gè)動(dòng)作,多核 cpu 的服務(wù)器,除了 GC 線程外,在 Stop The World 的時(shí)候都是會(huì)掛起的,直到 Stop The World 結(jié)束。以幾種老年代垃圾收集器為例:

 Serial Old 收集器,全程 Stop The World;

 Parallel Old 收集器,全程 Stop The World;

 CMS 收集器,它在初始標(biāo)記與并發(fā)標(biāo)記兩個(gè)過(guò)程中,為了準(zhǔn)確標(biāo)記出需要回收的對(duì)象,都會(huì) Stop The World,但是相比前兩種大大減少了系統(tǒng)停頓時(shí)間。

無(wú)論如何,當(dāng)真正發(fā)生 Stop The World 的時(shí)候,就會(huì)出現(xiàn) GC 線程在占用 cpu 工作而其他線程掛起的情況,自然表現(xiàn)也就為某個(gè) cpu 的 us 很高而且他 cpu 的 us 很低。

針對(duì) FullGC 的問題,排查思路通常為:

 ps -ef | grep java,查詢 Java 應(yīng)用的進(jìn)程 pid;

 jstat -gcutil pid 1000 1000,每隔 1 秒打印一次內(nèi)存情況共打印 1000 次,觀察老年代(O)、MetaSpace(MU)的內(nèi)存使用率與 FullGC 次數(shù);

  確認(rèn)有頻繁的 FullGC 的發(fā)生,查看 GC 日志,每個(gè)應(yīng)用 GC 日志配置的路徑不同;

 jmap -dump:format=b,file=filename pid,保留現(xiàn)場(chǎng);

  重啟應(yīng)用,迅速止血,避免引起更大的線上問題;

 dump 出來(lái)的內(nèi)容,結(jié)合 MAT 分析工具分析內(nèi)存情況,排查 FullGC 出現(xiàn)的原因。

如果 FullGC 只是發(fā)生在老年代區(qū),比較有經(jīng)驗(yàn)的開發(fā)人員還是容易發(fā)現(xiàn)問題的,一般都是一些代碼 bug 引起的。MetaSpace 發(fā)生的 FullGC 經(jīng)常會(huì)是一些詭異、隱晦的問題,很多和引入的第三方框架使用不當(dāng)有關(guān)或者就是第三方框架有 bug 導(dǎo)致的,排查起來(lái)就很費(fèi)時(shí)間。

那么頻繁 FullGC 之后最終會(huì)導(dǎo)致 load 如何變化呢?這個(gè)我沒有驗(yàn)證過(guò)和看過(guò)具體數(shù)據(jù),只是通過(guò)理論分析,如果所有線程都是空閑的,只有 GC 線程在一直做 FullGC,那么 load 最后會(huì)趨近于 1。但是實(shí)際不可能,因?yàn)槿绻麤]有其他線程在運(yùn)行,怎么可能導(dǎo)致頻繁 FullGC 呢。所以,在其他線程處理任務(wù)的情況下 Stop The World 之后,cpu 掛起,任務(wù)得不到處理,更大可能的是 load 會(huì)一直升高。

最后順便提一句,前面一直在講 FullGC,頻繁的 YoungGC 也是會(huì)導(dǎo)致 load 升高的,之前看到過(guò)的一個(gè)案例是,Object 轉(zhuǎn) xml,xml 轉(zhuǎn) Object,代碼中每處都 new XStream() 去進(jìn)行 xml 序列化與反序列化,回收速度跟不上 new 的速度,YoungGC 次數(shù)陡增。

六、load 高、cpu 低的問題排查思路

關(guān)于 load 的部分,我們可以看到會(huì)導(dǎo)致 load 高的幾個(gè)因素:

  線程正在使用 cpu;

  線程正在等待使用 cpu;

  線程在執(zhí)行不可被打斷的 IO 操作。

既然 cpu 不高,load 高,那么線程要么在進(jìn)行 io 要么在等待使用 cpu。不過(guò)對(duì)于后者 等待使用 cpu 我這里存疑,比如線程池里面 10 個(gè)線程,任務(wù)來(lái)的很慢,每次只會(huì)用到 1 個(gè)線程,那么 9 個(gè)線程都是在等待使用 cpu,但是這 9 個(gè)線程明顯是不會(huì)占據(jù)系統(tǒng)資源的,因此我認(rèn)為自然也不會(huì)消耗 cpu,所以這個(gè)點(diǎn)不考慮。

因此,在 cpu 不高的情況下假如 load 高,大概率 io 高才是罪魁禍?zhǔn)祝鼘?dǎo)致的是任務(wù)一直在跑,遲遲處理不完,線程無(wú)法回歸線程池中。首先簡(jiǎn)單講講磁盤 io,既然 wa 表示的是磁盤 io 等待 cpu 的百分比,那么我們可以看下 wa 確認(rèn)下是不是磁盤 io 導(dǎo)致的:

如果是,那么按照 cpu 高同樣的方式打印一下堆棧,查看文件 io 的部分進(jìn)行分析,排查原因,例如是不是多線程都在讀取本地一個(gè)超大的文件到內(nèi)存。

磁盤 io 導(dǎo)致的 load 高,我相信這畢竟是少數(shù),因?yàn)?Java 語(yǔ)言的特點(diǎn),應(yīng)用程序更多的高 io 應(yīng)當(dāng)是在處理網(wǎng)絡(luò)請(qǐng)求,例如:

  從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù);

  從 Redis 中獲取數(shù)據(jù);

  調(diào)用 Http 接口從支付寶獲取數(shù)據(jù);

  通過(guò) dubbo 獲取某服務(wù)中的數(shù)據(jù)。

針對(duì)這種情況,我覺得首先我們應(yīng)該對(duì)整個(gè)系統(tǒng)架構(gòu)的依賴比較熟悉,例如我畫一個(gè)草圖:

對(duì)依賴方的調(diào)用任何一個(gè)出現(xiàn)比較高的耗時(shí)都會(huì)增加自身系統(tǒng)的 load,出現(xiàn) load 高的建議排查方式為:

  查日志,無(wú)論是 HBase、MySql、Redis 調(diào)用還是通過(guò) http、dubbo 調(diào)用接口,調(diào)用超時(shí),拿連接池中的連接超時(shí),通常都會(huì)有錯(cuò)誤日志拋出來(lái),只要系統(tǒng)里面沒有捕獲異常之后不打日志直接吞掉一般都能查到相關(guān)的異常;

  對(duì)于 dubbo、http 的調(diào)用,建議做好監(jiān)控埋點(diǎn),輸出接口名、方法入?yún)ⅲ刂拼笮。⑹欠癯晒Α⒄{(diào)用時(shí)長(zhǎng)等必要參數(shù),有些時(shí)候可能沒有超時(shí),但是調(diào)用 2 秒、3 秒一樣會(huì)導(dǎo)致 load 升高,所以這種時(shí)候需要查看方法調(diào)用時(shí)長(zhǎng)進(jìn)行下一步動(dòng)作。

如果上面的步驟還是沒用或者沒有對(duì)接口調(diào)用做埋點(diǎn),那么還是萬(wàn)能的打印堆棧吧,連續(xù)打印五次十次,看一下每次的堆棧是否大多都指向同一個(gè)接口的調(diào)用,網(wǎng)絡(luò) io 的話,堆棧的最后幾行一般都有 at java.net.SocketInputStream.read(SocketInputStream.java:129)。

七、Java 應(yīng)用 load 高的幾種原因總結(jié)

前面說(shuō)了這么多,這里總結(jié)一下 load 高可能的一些原因:

  死循環(huán)或者不合理的大量循環(huán)操作,如果不是循環(huán)操作,按照現(xiàn)代 cpu 的處理速度來(lái)說(shuō)處理一大段代碼也就一會(huì)會(huì)兒的事,基本對(duì)能力無(wú)消耗;

  頻繁的 YoungGC;

  頻繁的 FullGC;

  高磁盤 IO;

  高網(wǎng)絡(luò) IO。

系統(tǒng) load 高通常都是由于某段發(fā)布的代碼有 bug 或者引入某些第三方 jar 而又使用不合理導(dǎo)致的,因此注意首先區(qū)分 load 高,是由于 cpu 高導(dǎo)致的還是 io 高導(dǎo)致的,根據(jù)不同的場(chǎng)景采取不同定位問題的方式。

當(dāng)束手無(wú)策時(shí),jstack 打印堆棧多分析分析吧,或許能靈光一現(xiàn)能找到錯(cuò)誤原因。

感謝你能夠認(rèn)真閱讀完這篇文章,希望丸趣 TV 小編分享的“l(fā)inux 中如何排查 CPU 與 Load 異常問題”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持丸趣 TV,關(guān)注丸趣 TV 行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-25發(fā)表,共計(jì)6113字。
轉(zhuǎn)載說(shuō)明:除特殊說(shuō)明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請(qǐng)注明出處。
評(píng)論(沒有評(píng)論)
主站蜘蛛池模板: 长阳| 大荔县| 元朗区| 中宁县| 美姑县| 江口县| 弋阳县| 青铜峡市| 城市| 岢岚县| 集贤县| 方山县| 祁门县| 阳谷县| 汝阳县| 涿州市| 蓬安县| 论坛| 舟山市| 民勤县| 宁德市| 乌鲁木齐县| 阿坝县| 平乡县| 盱眙县| 渑池县| 祥云县| 临猗县| 明光市| 宁波市| 内丘县| 紫云| 大石桥市| 芦溪县| 佛教| 遵化市| 车险| 井研县| 大邑县| 公安县| 砀山县|