《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 可編程邏輯 > 其他 > Linux 性能優(yōu)化的全景指南

Linux 性能優(yōu)化的全景指南

2022-11-29
作者: 電子技術(shù)應(yīng)用專欄作家 一口Linux
來源: 電子技術(shù)應(yīng)用專欄作家 一口Linux
關(guān)鍵詞: Linux 性能優(yōu)化

  Linux 性能優(yōu)化

  性能優(yōu)化

  性能指標(biāo)

  高并發(fā)和響應(yīng)快對應(yīng)著性能優(yōu)化的兩個(gè)核心指標(biāo):吞吐和延時(shí)

  微信截圖_20221129140217.png

  應(yīng)用負(fù)載角度:直接影響了產(chǎn)品終端的用戶體驗(yàn)

  系統(tǒng)資源角度:資源使用率,、飽和度等

  性能問題的本質(zhì)就是系統(tǒng)資源已經(jīng)到達(dá)瓶頸,但請求的處理還不夠快,無法支撐更多的請求。性能分析實(shí)際上就是找出應(yīng)用或系統(tǒng)的瓶頸,,設(shè)法去避免或緩解它們,。

  選擇指標(biāo)評估應(yīng)用程序和系統(tǒng)性能

  為應(yīng)用程序和系統(tǒng)設(shè)置性能目標(biāo)

  進(jìn)行性能基準(zhǔn)測試

  性能分析定位瓶頸

  性能監(jiān)控和告警

  對于不同的性能問題要選取不同的性能分析工具,。下面是常用的Linux Performance Tools以及對應(yīng)分析的性能問題類型,。

  微信截圖_20221129140908.png

  到底應(yīng)該怎么理解”平均負(fù)載”

  平均負(fù)載:單位時(shí)間內(nèi),,系統(tǒng)處于可運(yùn)行狀態(tài)和不可中斷狀態(tài)的平均進(jìn)程數(shù),,也就是平均活躍進(jìn)程數(shù)。它和我們傳統(tǒng)意義上理解的CPU使用率并沒有直接關(guān)系,。

  其中不可中斷進(jìn)程是正處于內(nèi)核態(tài)關(guān)鍵流程中的進(jìn)程(如常見的等待設(shè)備的I/O響應(yīng)),。不可中斷狀態(tài)實(shí)際上是系統(tǒng)對進(jìn)程和硬件設(shè)備的一種保護(hù)機(jī)制。

  平均負(fù)載多少時(shí)合理

  實(shí)際生產(chǎn)環(huán)境中將系統(tǒng)的平均負(fù)載監(jiān)控起來,,根據(jù)歷史數(shù)據(jù)判斷負(fù)載的變化趨勢,。當(dāng)負(fù)載存在明顯升高趨勢時(shí),及時(shí)進(jìn)行分析和調(diào)查,。當(dāng)然也可以當(dāng)設(shè)置閾值(如當(dāng)平均負(fù)載高于CPU數(shù)量的70%時(shí))

  現(xiàn)實(shí)工作中我們會經(jīng)?;煜骄?fù)載和CPU使用率的概念,其實(shí)兩者并不完全對等:

  CPU 密集型進(jìn)程,,大量 CPU 使用會導(dǎo)致平均負(fù)載升高,,此時(shí)兩者一致

  I/O 密集型進(jìn)程,等待 I/O 也會導(dǎo)致平均負(fù)載升高,,此時(shí) CPU 使用率并不一定高

  大量等待 CPU 的進(jìn)程調(diào)度會導(dǎo)致平均負(fù)載升高,,此時(shí) CPU 使用率也會比較高

  平均負(fù)載高時(shí)可能是 CPU 密集型進(jìn)程導(dǎo)致,也可能是 I/O 繁忙導(dǎo)致,。具體分析時(shí)可以結(jié)合 mpstat/pidstat 工具輔助分析負(fù)載來源,。

  CPU

  CPU上下文切換(上)

  CPU 上下文切換,就是把前一個(gè)任務(wù)的 CPU 上下文(CPU 寄存器和 PC)保存起來,,然后加載新任務(wù)的上下文到這些寄存器和程序計(jì)數(shù)器,,最后再跳轉(zhuǎn)到程序計(jì)數(shù)器所指的位置,運(yùn)行新任務(wù),。其中,,保存下來的上下文會存儲在系統(tǒng)內(nèi)核中,待任務(wù)重新調(diào)度執(zhí)行時(shí)再加載,,保證原來的任務(wù)狀態(tài)不受影響,。

  按照任務(wù)類型,CPU 上下文切換分為:

  進(jìn)程上下文切換

  線程上下文切換

  中斷上下文切換

  進(jìn)程上下文切換

  Linux 進(jìn)程按照等級權(quán)限將進(jìn)程的運(yùn)行空間分為內(nèi)核空間和用戶空間,。從用戶態(tài)向內(nèi)核態(tài)轉(zhuǎn)變時(shí)需要通過系統(tǒng)調(diào)用來完成,。

  一次系統(tǒng)調(diào)用過程其實(shí)進(jìn)行了兩次 CPU 上下文切換:

  CPU 寄存器中用戶態(tài)的指令位置先保存起來,CPU 寄存器更新為內(nèi)核態(tài)指令的位置,,跳轉(zhuǎn)到內(nèi)核態(tài)運(yùn)行內(nèi)核任務(wù),;

  系統(tǒng)調(diào)用結(jié)束后,CPU 寄存器恢復(fù)原來保存的用戶態(tài)數(shù)據(jù),再切換到用戶空間繼續(xù)運(yùn)行,。

  系統(tǒng)調(diào)用過程中并不會涉及虛擬內(nèi)存等進(jìn)程用戶態(tài)資源,,也不會切換進(jìn)程。和傳統(tǒng)意義上的進(jìn)程上下文切換不同,。因此系統(tǒng)調(diào)用通常稱為特權(quán)模式切換,。

  進(jìn)程是由內(nèi)核管理和調(diào)度的,進(jìn)程上下文切換只能發(fā)生在內(nèi)核態(tài),。因此相比系統(tǒng)調(diào)用來說,,在保存當(dāng)前進(jìn)程的內(nèi)核狀態(tài)和CPU寄存器之前,需要先把該進(jìn)程的虛擬內(nèi)存,,棧保存下來,。再加載新進(jìn)程的內(nèi)核態(tài)后,還要刷新進(jìn)程的虛擬內(nèi)存和用戶棧,。

  進(jìn)程只有在調(diào)度到CPU上運(yùn)行時(shí)才需要切換上下文,,有以下幾種場景:CPU時(shí)間片輪流分配,系統(tǒng)資源不足導(dǎo)致進(jìn)程掛起,,進(jìn)程通過sleep函數(shù)主動掛起,,高優(yōu)先級進(jìn)程搶占時(shí)間片,硬件中斷時(shí)CPU上的進(jìn)程被掛起轉(zhuǎn)而執(zhí)行內(nèi)核中的中斷服務(wù),。

  線程上下文切換

  線程上下文切換分為兩種:

  前后線程同屬于一個(gè)進(jìn)程,,切換時(shí)虛擬內(nèi)存資源不變,只需要切換線程的私有數(shù)據(jù),,寄存器等,;

  前后線程屬于不同進(jìn)程,與進(jìn)程上下文切換相同,。

  同進(jìn)程的線程切換消耗資源較少,,這也是多線程的優(yōu)勢。

  中斷上下文切換

  中斷上下文切換并不涉及到進(jìn)程的用戶態(tài),,因此中斷上下文只包括內(nèi)核態(tài)中斷服務(wù)程序執(zhí)行所必須的狀態(tài)(CPU寄存器,,內(nèi)核堆棧,硬件中斷參數(shù)等),。

  中斷處理優(yōu)先級比進(jìn)程高,,所以中斷上下文切換和進(jìn)程上下文切換不會同時(shí)發(fā)生

  CPU上下文切換(下)

  通過 vmstat 可以查看系統(tǒng)總體的上下文切換情況

微信截圖_20221129141333.png

  cs (context switch) 每秒上下文切換次數(shù)

  in (interrupt) 每秒中斷次數(shù)

  r (runnning or runnable)就緒隊(duì)列的長度,正在運(yùn)行和等待CPU的進(jìn)程數(shù)

  b (Blocked) 處于不可中斷睡眠狀態(tài)的進(jìn)程數(shù)

  要查看每個(gè)進(jìn)程的詳細(xì)情況,,需要使用pidstat來查看每個(gè)進(jìn)程上下文切換情況

 微信截圖_20221129141408.png

  cswch 每秒自愿上下文切換次數(shù)(進(jìn)程無法獲取所需資源導(dǎo)致的上下文切換)

  nvcswch 每秒非自愿上下文切換次數(shù)(時(shí)間片輪流等系統(tǒng)強(qiáng)制調(diào)度)

 微信截圖_20221129141430.png

  微信截圖_20221129141452.png

  發(fā)現(xiàn)次數(shù)變化速度最快的是重調(diào)度中斷(RES),,該中斷用來喚醒空閑狀態(tài)的CPU來調(diào)度新的任務(wù)運(yùn)行。分析還是因?yàn)檫^多任務(wù)的調(diào)度問題,,和上下文切換分析一致,。

  某個(gè)應(yīng)用的CPU使用率達(dá)到100%,,怎么辦?

  Linux作為多任務(wù)操作系統(tǒng),,將CPU時(shí)間劃分為很短的時(shí)間片,,通過調(diào)度器輪流分配給各個(gè)任務(wù)使用。為了維護(hù)CPU時(shí)間,,Linux通過事先定義的節(jié)拍率,,觸發(fā)時(shí)間中斷,并使用全局變了jiffies記錄開機(jī)以來的節(jié)拍數(shù),。時(shí)間中斷發(fā)生一次該值+1.

  CPU使用率,除了空閑時(shí)間以外的其他時(shí)間占總CPU時(shí)間的百分比,??梢酝ㄟ^/proc/stat中的數(shù)據(jù)來計(jì)算出CPU使用率。因?yàn)?proc/stat時(shí)開機(jī)以來的節(jié)拍數(shù)累加值,,計(jì)算出來的是開機(jī)以來的平均CPU使用率,,一般意義不大??梢蚤g隔取一段時(shí)間的兩次值作差來計(jì)算該段時(shí)間內(nèi)的平均CPU使用率,。性能分析工具給出的都是間隔一段時(shí)間的平均CPU使用率,要注意間隔時(shí)間的設(shè)置,。

  CPU使用率可以通過top 或 ps來查看,。分析進(jìn)程的CPU問題可以通過perf,它以性能事件采樣為基礎(chǔ),,不僅可以分析系統(tǒng)的各種事件和內(nèi)核性能,,還可以用來分析指定應(yīng)用程序的性能問題。

  微信截圖_20221129141553.png

  發(fā)現(xiàn)此時(shí)每秒可承受請求給長少,,此時(shí)將測試的請求數(shù)從100增加到10000,。在另外一個(gè)終端運(yùn)行top查看每個(gè)CPU的使用率。發(fā)現(xiàn)系統(tǒng)中幾個(gè)php-fpm進(jìn)程導(dǎo)致CPU使用率驟升,。

  微信截圖_20221129141637.png

  實(shí)驗(yàn)結(jié)果中每秒請求數(shù)依舊不高,,我們將并發(fā)請求數(shù)降為5后,nginx負(fù)載能力依舊很低,。

  此時(shí)用top和pidstat發(fā)現(xiàn)系統(tǒng)CPU使用率過高,,但是并沒有發(fā)現(xiàn)CPU使用率高的進(jìn)程。

  出現(xiàn)這種情況一般時(shí)我們分析時(shí)遺漏的什么信息,,重新運(yùn)行top命令并觀察一會,。發(fā)現(xiàn)就緒隊(duì)列中處于Running狀態(tài)的進(jìn)行過多,超過了我們的并發(fā)請求次數(shù)5. 再仔細(xì)查看進(jìn)程運(yùn)行數(shù)據(jù),,發(fā)現(xiàn)nginx和php-fpm都處于sleep狀態(tài),,真正處于運(yùn)行的卻是幾個(gè)stress進(jìn)程,。

  下一步就利用pidstat分析這幾個(gè)stress進(jìn)程,發(fā)現(xiàn)沒有任何輸出,。用ps aux交叉驗(yàn)證發(fā)現(xiàn)依舊不存在該進(jìn)程,。說明不是工具的問題。再top查看發(fā)現(xiàn)stress進(jìn)程的進(jìn)程號變化了,,此時(shí)有可能時(shí)以下兩種原因?qū)е拢?/p>

  進(jìn)程不停的崩潰重啟(如段錯(cuò)誤/配置錯(cuò)誤等),,此時(shí)進(jìn)程退出后可能又被監(jiān)控系統(tǒng)重啟;

  短時(shí)進(jìn)程導(dǎo)致,,即其他應(yīng)用內(nèi)部通過 exec 調(diào)用的外面命令,,這些命令一般只運(yùn)行很短時(shí)間就結(jié)束,很難用top這種間隔較長的工具來發(fā)現(xiàn)

  可以通過pstree來查找 stress 的父進(jìn)程,,找出調(diào)用關(guān)系,。

微信截圖_20221129141707.png

  發(fā)現(xiàn)是php-fpm調(diào)用的該子進(jìn)程,此時(shí)去查看源碼可以看出每個(gè)請求都會調(diào)用一個(gè)stress命令來模擬I/O壓力,。之前top顯示的結(jié)果是CPU使用率升高,,是否真的是由該stress命令導(dǎo)致的,還需要繼續(xù)分析,。代碼中給每個(gè)請求加了verbose=1的參數(shù)后可以查看stress命令的輸出,,在中斷測試該命令結(jié)果顯示stress命令運(yùn)行時(shí)存在因權(quán)限問題導(dǎo)致的文件創(chuàng)建失敗的bug。

  此時(shí)依舊只是猜測,,下一步繼續(xù)通過perf工具來分析,。性能報(bào)告顯示確實(shí)時(shí)stress占用了大量的CPU,通過修復(fù)權(quán)限問題來優(yōu)化解決即可,。

  系統(tǒng)中出現(xiàn)大量不可中斷進(jìn)程和僵尸進(jìn)程怎么辦,?

  進(jìn)程狀態(tài)

  R Running/Runnable,表示進(jìn)程在CPU的就緒隊(duì)列中,,正在運(yùn)行或者等待運(yùn)行,;

  D Disk Sleep,不可中斷狀態(tài)睡眠,,一般表示進(jìn)程正在跟硬件交互,,并且交互過程中不允許被其他進(jìn)程中斷;

  Z Zombie,,僵尸進(jìn)程,,表示進(jìn)程實(shí)際上已經(jīng)結(jié)束,但是父進(jìn)程還沒有回收它的資源,;

  S Interruptible Sleep,,可中斷睡眠狀態(tài),表示進(jìn)程因?yàn)榈却硞€(gè)事件而被系統(tǒng)掛起,,當(dāng)?shù)却录l(fā)生則會被喚醒并進(jìn)入R狀態(tài),;

  I Idle,,空閑狀態(tài),用在不可中斷睡眠的內(nèi)核線程上,。該狀態(tài)不會導(dǎo)致平均負(fù)載升高,;

  T Stop/Traced,表示進(jìn)程處于暫?;蚋櫊顟B(tài)(SIGSTOP/SIGCONT,, GDB調(diào)試);

  X Dead,,進(jìn)程已經(jīng)消亡,,不會在top/ps中看到。

  對于不可中斷狀態(tài),,一般都是在很短時(shí)間內(nèi)結(jié)束,,可忽略。但是如果系統(tǒng)或硬件發(fā)生故障,,進(jìn)程可能會保持不可中斷狀態(tài)很久,,甚至系統(tǒng)中出現(xiàn)大量不可中斷狀態(tài),,此時(shí)需注意是否出現(xiàn)了I/O性能問題,。

  僵尸進(jìn)程一般多進(jìn)程應(yīng)用容易遇到,父進(jìn)程來不及處理子進(jìn)程狀態(tài)時(shí)子進(jìn)程就提前退出,,此時(shí)子進(jìn)程就變成了僵尸進(jìn)程,。大量的僵尸進(jìn)程會用盡PID進(jìn)程號,導(dǎo)致新進(jìn)程無法建立,。

微信截圖_20221129141728.png

  可以看到此時(shí)有多個(gè)app進(jìn)程運(yùn)行,,狀態(tài)分別時(shí)Ss+和D+。其中后面s表示進(jìn)程是一個(gè)會話的領(lǐng)導(dǎo)進(jìn)程,,+號表示前臺進(jìn)程組,。

  其中進(jìn)程組表示一組相互關(guān)聯(lián)的進(jìn)程,子進(jìn)程是父進(jìn)程所在組的組員,。會話指共享同一個(gè)控制終端的一個(gè)或多個(gè)進(jìn)程組,。

  用top查看系統(tǒng)資源發(fā)現(xiàn):1)平均負(fù)載在逐漸增加,且1分鐘內(nèi)平均負(fù)載達(dá)到了CPU個(gè)數(shù),,說明系統(tǒng)可能已經(jīng)有了性能瓶頸,;2)僵尸進(jìn)程比較多且在不停增加;3)us和sys CPU使用率都不高,,iowait卻比較高,;4)每個(gè)進(jìn)程CPU使用率也不高,但有兩個(gè)進(jìn)程處于D狀態(tài),,可能在等待IO,。

  分析目前數(shù)據(jù)可知:iowait過高導(dǎo)致系統(tǒng)平均負(fù)載升高,,僵尸進(jìn)程不斷增長說明有程序沒能正確清理子進(jìn)程資源。

  用dstat來分析,,因?yàn)樗梢酝瑫r(shí)查看CPU和I/O兩種資源的使用情況,,便于對比分析。

微信截圖_20221129141844.png

  可以看到當(dāng)wai(iowait)升高時(shí)磁盤請求read都會很大,,說明iowait的升高和磁盤的讀請求有關(guān),。接下來分析到底時(shí)哪個(gè)進(jìn)程在讀磁盤。

  之前 Top 查看的處于 D 狀態(tài)的進(jìn)程號,,用 pidstat -d -p XXX 展示進(jìn)程的 I/O 統(tǒng)計(jì)數(shù)據(jù),。發(fā)現(xiàn)處于 D 狀態(tài)的進(jìn)程都沒有任何讀寫操作。在用 pidstat -d 查看所有進(jìn)程的 I/O統(tǒng)計(jì)數(shù)據(jù),,看到 app 進(jìn)程在進(jìn)行磁盤讀操作,,每秒讀取 32MB 的數(shù)據(jù)。進(jìn)程訪問磁盤必須使用系統(tǒng)調(diào)用處于內(nèi)核態(tài),,接下來重點(diǎn)就是找到app進(jìn)程的系統(tǒng)調(diào)用,。

 微信截圖_20221129141900.png

  報(bào)錯(cuò)沒有權(quán)限,因?yàn)橐呀?jīng)時(shí) root 權(quán)限了,。所以遇到這種情況,,首先要檢查進(jìn)程狀態(tài)是否正常。ps 命令查找該進(jìn)程已經(jīng)處于Z狀態(tài),,即僵尸進(jìn)程,。

  這種情況下top pidstat之類的工具無法給出更多的信息,此時(shí)像第5篇一樣,,用 perf record -d和 perf report 進(jìn)行分析,,查看app進(jìn)程調(diào)用棧。

  看到 app 確實(shí)在通過系統(tǒng)調(diào)用 sys_read() 讀取數(shù)據(jù),,并且從 new_sync_read和 blkdev_direct_IO看出進(jìn)程時(shí)進(jìn)行直接讀操作,,請求直接從磁盤讀,沒有通過緩存導(dǎo)致iowait升高,。

  通過層層分析后,,root cause 是 app 內(nèi)部進(jìn)行了磁盤的直接I/O。然后定位到具體代碼位置進(jìn)行優(yōu)化即可,。

  僵尸進(jìn)程

  上述優(yōu)化后 iowait 顯著下降,,但是僵尸進(jìn)程數(shù)量仍舊在增加。首先要定位僵尸進(jìn)程的父進(jìn)程,,通過pstree -aps XXX,,打印出該僵尸進(jìn)程的調(diào)用樹,發(fā)現(xiàn)父進(jìn)程就是app進(jìn)程,。

  查看app代碼,,看看子進(jìn)程結(jié)束的處理是否正確(是否調(diào)用wait()/waitpid(),,有沒有注冊SIGCHILD信號的處理函數(shù)等)。

  碰到iowait升高時(shí),,先用dstat pidstat等工具確認(rèn)是否存在磁盤I/O問題,,再找是哪些進(jìn)程導(dǎo)致I/O,不能用strace直接分析進(jìn)程調(diào)用時(shí)可以通過perf工具分析,。

  對于僵尸問題,,用pstree找到父進(jìn)程,然后看源碼檢查子進(jìn)程結(jié)束的處理邏輯即可,。

  CPU性能指標(biāo)

  CPU使用率

  用戶CPU使用率, 包括用戶態(tài)(user)和低優(yōu)先級用戶態(tài)(nice). 該指標(biāo)過高說明應(yīng)用程序比較繁忙.

  系統(tǒng)CPU使用率, CPU在內(nèi)核態(tài)運(yùn)行的時(shí)間百分比(不含中斷). 該指標(biāo)高說明內(nèi)核比較繁忙.

  等待I/O的CPU使用率, iowait, 該指標(biāo)高說明系統(tǒng)與硬件設(shè)備I/O交互時(shí)間比較長.

  軟/硬中斷CPU使用率, 該指標(biāo)高說明系統(tǒng)中發(fā)生大量中斷.

  steal CPU / guest CPU, 表示虛擬機(jī)占用的CPU百分比.

  平均負(fù)載

  理想情況下平均負(fù)載等于邏輯CPU個(gè)數(shù),表示每個(gè)CPU都被充分利用. 若大于則說明系統(tǒng)負(fù)載較重.

  進(jìn)程上下文切換

  包括無法獲取資源的自愿切換和系統(tǒng)強(qiáng)制調(diào)度時(shí)的非自愿切換. 上下文切換本身是保證Linux正常運(yùn)行的一項(xiàng)核心功能. 過多的切換則會將原本運(yùn)行進(jìn)程的CPU時(shí)間消耗在寄存器,內(nèi)核占及虛擬內(nèi)存等數(shù)據(jù)保存和恢復(fù)上

  CPU緩存命中率

  CPU緩存的復(fù)用情況,命中率越高性能越好. 其中L1/L2常用在單核,L3則用在多核中

  性能工具

  平均負(fù)載案例

  先用uptime查看系統(tǒng)平均負(fù)載

  判斷負(fù)載在升高后再用mpstat和pidstat分別查看每個(gè)CPU和每個(gè)進(jìn)程CPU使用情況.找出導(dǎo)致平均負(fù)載較高的進(jìn)程.

  上下文切換案例

  先用vmstat查看系統(tǒng)上下文切換和中斷次數(shù)

  再用pidstat觀察進(jìn)程的自愿和非自愿上下文切換情況

  最后通過pidstat觀察線程的上下文切換情況

  進(jìn)程CPU使用率高案例

  先用top查看系統(tǒng)和進(jìn)程的CPU使用情況,定位到進(jìn)程

  再用perf top觀察進(jìn)程調(diào)用鏈,定位到具體函數(shù)

  系統(tǒng)CPU使用率高案例

  先用top查看系統(tǒng)和進(jìn)程的CPU使用情況,top/pidstat都無法找到CPU使用率高的進(jìn)程

  重新審視top輸出

  從CPU使用率不高,但是處于Running狀態(tài)的進(jìn)程入手

  perf record/report發(fā)現(xiàn)短時(shí)進(jìn)程導(dǎo)致 (execsnoop工具)

  不可中斷和僵尸進(jìn)程案例

  先用top觀察iowait升高,發(fā)現(xiàn)大量不可中斷和僵尸進(jìn)程

  strace無法跟蹤進(jìn)程系統(tǒng)調(diào)用

  perf分析調(diào)用鏈發(fā)現(xiàn)根源來自磁盤直接I/O

  軟中斷案例

  top觀察系統(tǒng)軟中斷CPU使用率高

  查看/proc/softirqs找到變化速率較快的幾種軟中斷

  sar命令發(fā)現(xiàn)是網(wǎng)絡(luò)小包問題

  tcpdump找出網(wǎng)絡(luò)幀的類型和來源,,確定SYN FLOOD攻擊導(dǎo)致

  根據(jù)不同的性能指標(biāo)來找合適的工具:

  微信截圖_20221129141953.png

  先運(yùn)行幾個(gè)支持指標(biāo)較多的工具,如 top/vmstat/pidstat,,根據(jù)它們的輸出可以得出是哪種類型的性能問題,。定位到進(jìn)程后再用 strace/perf 分析調(diào)用情況進(jìn)一步分析。如果是軟中斷導(dǎo)致用 /proc/softirqs

  微信截圖_20221129142025.png

  CPU優(yōu)化

  應(yīng)用程序優(yōu)化

  編譯器優(yōu)化:編譯階段開啟優(yōu)化選項(xiàng),,如gcc -O2

  算法優(yōu)化

  異步處理:避免程序因?yàn)榈却硞€(gè)資源而一直阻塞,,提升程序的并發(fā)處理能力。(將輪詢替換為事件通知)

  多線程代替多進(jìn)程:減少上下文切換成本

  善用緩存:加快程序處理速度

  系統(tǒng)優(yōu)化

  CPU綁定:將進(jìn)程綁定要1個(gè)/多個(gè)CPU上,,提高CPU緩存命中率,,減少CPU調(diào)度帶來的上下文切換

  CPU獨(dú)占:CPU親和性機(jī)制來分配進(jìn)程

  優(yōu)先級調(diào)整:使用nice適當(dāng)降低非核心應(yīng)用的優(yōu)先級

  為進(jìn)程設(shè)置資源顯示: cgroups設(shè)置使用上限,防止由某個(gè)應(yīng)用自身問題耗盡系統(tǒng)資源

  NUMA優(yōu)化: CPU盡可能訪問本地內(nèi)存

  中斷負(fù)載均衡: irpbalance,,將中斷處理過程自動負(fù)載均衡到各個(gè)CPU上

  TPS,、QPS,、系統(tǒng)吞吐量的區(qū)別和理解

  QPS(TPS)

  并發(fā)數(shù)

  響應(yīng)時(shí)間

  QPS(TPS)=并發(fā)數(shù)/平均相應(yīng)時(shí)間

  用戶請求服務(wù)器

  服務(wù)器內(nèi)部處理

  服務(wù)器返回給客戶

  QPS 類似 TPS,,但是對于一個(gè)頁面的訪問形成一個(gè) TPS,但是一次頁面請求可能包含多次對服務(wù)器的請求,,可能計(jì)入多次 QPS

  QPS(Queries Per Second)每秒查詢率,,一臺服務(wù)器每秒能夠響應(yīng)的查詢次數(shù).

  TPS(Transactions Per Second)每秒事務(wù)數(shù),軟件測試的結(jié)果.

  系統(tǒng)吞吐量,,包括幾個(gè)重要參數(shù):

  內(nèi)存

  Linux內(nèi)存是怎么工作的

  內(nèi)存映射

  大多數(shù)計(jì)算機(jī)用的主存都是動態(tài)隨機(jī)訪問內(nèi)存(DRAM),,只有內(nèi)核才可以直接訪問物理內(nèi)存。Linux內(nèi)核給每個(gè)進(jìn)程提供了一個(gè)獨(dú)立的虛擬地址空間,,并且這個(gè)地址空間是連續(xù)的,。這樣進(jìn)程就可以很方便的訪問內(nèi)存(虛擬內(nèi)存)。

  虛擬地址空間的內(nèi)部分為內(nèi)核空間和用戶空間兩部分,,不同字長的處理器地址空間的范圍不同,。32位系統(tǒng)內(nèi)核空間占用1G,用戶空間占3G,。64位系統(tǒng)內(nèi)核空間和用戶空間都是128T,,分別占內(nèi)存空間的最高和最低處,,中間部分為未定義。

  并不是所有的虛擬內(nèi)存都會分配物理內(nèi)存,,只有實(shí)際使用的才會,。分配后的物理內(nèi)存通過內(nèi)存映射管理。為了完成內(nèi)存映射,,內(nèi)核為每個(gè)進(jìn)程都維護(hù)了一個(gè)頁表,,記錄虛擬地址和物理地址的映射關(guān)系。頁表實(shí)際存儲在CPU的內(nèi)存管理單元MMU中,,處理器可以直接通過硬件找出要訪問的內(nèi)存,。

  當(dāng)進(jìn)程訪問的虛擬地址在頁表中查不到時(shí),系統(tǒng)會產(chǎn)生一個(gè)缺頁異常,,進(jìn)入內(nèi)核空間分配物理內(nèi)存,,更新進(jìn)程頁表,再返回用戶空間恢復(fù)進(jìn)程的運(yùn)行,。

  MMU以頁為單位管理內(nèi)存,,頁大小4KB。為了解決頁表項(xiàng)過多問題Linux提供了多級頁表和HugePage的機(jī)制,。

  虛擬內(nèi)存空間分布

  用戶空間內(nèi)存從低到高是五種不同的內(nèi)存段:

  只讀段 代碼和常量等

  數(shù)據(jù)段 全局變量等

  堆 動態(tài)分配的內(nèi)存,,從低地址開始向上增長

  文件映射 動態(tài)庫、共享內(nèi)存等,,從高地址開始向下增長

  棧 包括局部變量和函數(shù)調(diào)用的上下文等,,棧的大小是固定的。一般8MB

  內(nèi)存分配與回收

  分配

  malloc 對應(yīng)到系統(tǒng)調(diào)用上有兩種實(shí)現(xiàn)方式:

  brk() 針對小塊內(nèi)存(<128K),,通過移動堆頂位置來分配,。

  內(nèi)存釋放后不立即歸還內(nèi)存,而是被緩存起來,。

  mmap()針對大塊內(nèi)存(>128K),,直接用內(nèi)存映射來分配,即在文件映射段找一塊空閑內(nèi)存分配,。

  前者的緩存可以減少缺頁異常的發(fā)生,,提高內(nèi)存訪問效率。但是由于內(nèi)存沒有歸還系統(tǒng),,在內(nèi)存工作繁忙時(shí),,頻繁的內(nèi)存分配/釋放會造成內(nèi)存碎片。

  后者在釋放時(shí)直接歸還系統(tǒng),,所以每次mmap都會發(fā)生缺頁異常,。

  在內(nèi)存工作繁忙時(shí),頻繁內(nèi)存分配會導(dǎo)致大量缺頁異常,使內(nèi)核管理負(fù)擔(dān)增加,。

  上述兩種調(diào)用并沒有真正分配內(nèi)存,,這些內(nèi)存只有在首次訪問時(shí),才通過缺頁異常進(jìn)入內(nèi)核中,,由內(nèi)核來分配

  回收

  內(nèi)存緊張時(shí),,系統(tǒng)通過以下方式來回收內(nèi)存:

  回收緩存:LRU算法回收最近最少使用的內(nèi)存頁面;

  回收不常訪問內(nèi)存:把不常用的內(nèi)存通過交換分區(qū)寫入磁盤

  殺死進(jìn)程:OOM內(nèi)核保護(hù)機(jī)制(進(jìn)程消耗內(nèi)存越大 oom_score 越大,,占用 CPU 越多 oom_score 越小,,可以通過 /proc 手動調(diào)整 oom_adj)

 微信截圖_20221129142113.png

  如何查看內(nèi)存使用情況

  free來查看整個(gè)系統(tǒng)的內(nèi)存使用情況

  top/ps來查看某個(gè)進(jìn)程的內(nèi)存使用情況

  VIRT 進(jìn)程的虛擬內(nèi)存大小

  RES 常駐內(nèi)存的大小,即進(jìn)程實(shí)際使用的物理內(nèi)存大小,,不包括swap和共享內(nèi)存

  SHR 共享內(nèi)存大小,,與其他進(jìn)程共享的內(nèi)存,加載的動態(tài)鏈接庫以及程序代碼段

  %MEM 進(jìn)程使用物理內(nèi)存占系統(tǒng)總內(nèi)存的百分比

  怎樣理解內(nèi)存中的Buffer和Cache,?

  buffer是對磁盤數(shù)據(jù)的緩存,,cache是對文件數(shù)據(jù)的緩存,它們既會用在讀請求也會用在寫請求中

  如何利用系統(tǒng)緩存優(yōu)化程序的運(yùn)行效率

  緩存命中率

  緩存命中率是指直接通過緩存獲取數(shù)據(jù)的請求次數(shù),,占所有請求次數(shù)的百分比,。命中率越高說明緩存帶來的收益越高,應(yīng)用程序的性能也就越好,。

  安裝bcc包后可以通過cachestat和cachetop來監(jiān)測緩存的讀寫命中情況,。

  安裝pcstat后可以查看文件在內(nèi)存中的緩存大小以及緩存比例

 微信截圖_20221129142227.png

  dd緩存加速

  微信截圖_20221129142245.png

  O_DIRECT選項(xiàng)繞過系統(tǒng)緩存

微信截圖_20221129142305.png

  但是憑感覺可知如果緩存命中讀速度不應(yīng)如此慢,讀次數(shù)時(shí)1024,,頁大小為4K,,五秒的時(shí)間內(nèi)讀取了1024*4KB數(shù)據(jù),即每秒0.8MB,,和結(jié)果中32MB相差較大,。說明該案例沒有充分利用緩存,懷疑系統(tǒng)調(diào)用設(shè)置了直接I/O標(biāo)志繞過系統(tǒng)緩存,。因此接下來觀察系統(tǒng)調(diào)用,。

 微信截圖_20221129142335.png

  這就解釋了為什么讀32MB數(shù)據(jù)那么慢,,直接從磁盤讀寫肯定遠(yuǎn)遠(yuǎn)慢于緩存,。找出問題后我們再看案例的源代碼發(fā)現(xiàn)flags中指定了直接IO標(biāo)志。刪除該選項(xiàng)后重跑,,驗(yàn)證性能變化,。

  內(nèi)存泄漏,如何定位和處理,?

  對應(yīng)用程序來說,,動態(tài)內(nèi)存的分配和回收是核心又復(fù)雜的一個(gè)邏輯功能模塊。管理內(nèi)存的過程中會發(fā)生各種各樣的“事故”:

  沒正確回收分配的內(nèi)存,導(dǎo)致了泄漏

  訪問的是已分配內(nèi)存邊界外的地址,,導(dǎo)致程序異常退出

  內(nèi)存的分配與回收

  虛擬內(nèi)存分布從低到高分別是只讀段,,數(shù)據(jù)段,堆,,內(nèi)存映射段,,棧五部分。其中會導(dǎo)致內(nèi)存泄漏的是:

  堆:由應(yīng)用程序自己來分配和管理,,除非程序退出這些堆內(nèi)存不會被系統(tǒng)自動釋放,。

  內(nèi)存映射段:包括動態(tài)鏈接庫和共享內(nèi)存,其中共享內(nèi)存由程序自動分配和管理

  內(nèi)存泄漏的危害比較大,,這些忘記釋放的內(nèi)存,,不僅應(yīng)用程序自己不能訪問,系統(tǒng)也不能把它們再次分配給其他應(yīng)用,。內(nèi)存泄漏不斷累積甚至?xí)谋M系統(tǒng)內(nèi)存,。

  如何檢測內(nèi)存泄漏

微信截圖_20221129142359.png

  可以看到free在不斷下降,buffer和cache基本保持不變,。說明系統(tǒng)的內(nèi)存一致在升高,。但并不能說明存在內(nèi)存泄漏。此時(shí)可以通過memleak工具來跟蹤系統(tǒng)或進(jìn)程的內(nèi)存分配/釋放請求

  微信截圖_20221129142416.png

  從 memleak 輸出可以看到,,應(yīng)用在不停地分配內(nèi)存,,并且這些分配的地址并沒有被回收。通過調(diào)用??吹绞?fibonacci 函數(shù)分配的內(nèi)存沒有釋放,。定位到源碼后查看源碼來修復(fù)增加內(nèi)存釋放函數(shù)即可。

  為什么系統(tǒng)的 Swap 變高

  系統(tǒng)內(nèi)存資源緊張時(shí)通過內(nèi)存回收和OOM殺死進(jìn)程來解決,。其中可回收內(nèi)存包括:

  緩存/緩沖區(qū),,屬于可回收資源,在文件管理中通常叫做文件頁

  在應(yīng)用程序中通過fsync將臟頁同步到磁盤

  交給系統(tǒng),,內(nèi)核線程pdflush負(fù)責(zé)這些臟頁的刷新

  被應(yīng)用程序修改過暫時(shí)沒寫入磁盤的數(shù)據(jù)(臟頁),,要先寫入磁盤然后才能內(nèi)存釋放

  內(nèi)存映射獲取的文件映射頁,也可以被釋放掉,,下次訪問時(shí)從文件重新讀取

  對于程序自動分配的堆內(nèi)存,,也就是我們在內(nèi)存管理中的匿名頁,雖然這些內(nèi)存不能直接釋放,,但是 Linux 提供了 Swap 機(jī)制將不常訪問的內(nèi)存寫入到磁盤來釋放內(nèi)存,,再次訪問時(shí)從磁盤讀取到內(nèi)存即可。

  Swap原理

  Swap本質(zhì)就是把一塊磁盤空間或者一個(gè)本地文件當(dāng)作內(nèi)存來使用,,包括換入和換出兩個(gè)過程:

  換出:將進(jìn)程暫時(shí)不用的內(nèi)存數(shù)據(jù)存儲到磁盤中,,并釋放這些內(nèi)存

  換入:進(jìn)程再次訪問內(nèi)存時(shí),將它們從磁盤讀到內(nèi)存中

  Linux如何衡量內(nèi)存資源是否緊張?

  直接內(nèi)存回收新的大塊內(nèi)存分配請求,,但剩余內(nèi)存不足,。

  此時(shí)系統(tǒng)會回收一部分內(nèi)存;

  kswapd0 內(nèi)核線程定期回收內(nèi)存,。

  為了衡量內(nèi)存使用情況,,定義了pages_min,pages_low,,pages_high 三個(gè)閾值,,并根據(jù)其來進(jìn)行內(nèi)存的回收操作。

  剩余內(nèi)存 < pages_min,,進(jìn)程可用內(nèi)存耗盡了,,只有內(nèi)核才可以分配內(nèi)存

  pages_min < 剩余內(nèi)存 < pages_low,內(nèi)存壓力較大,kswapd0執(zhí)行內(nèi)存回收,,直到剩余內(nèi)存 > pages_high

  pages_low < 剩余內(nèi)存 < pages_high,,內(nèi)存有一定壓力,但可以滿足新內(nèi)存請求

  剩余內(nèi)存 > pages_high,,說明剩余內(nèi)存較多,,無內(nèi)存壓力

  pages_low = pages_min 5 / 4 pages_high = pages_min 3 / 2

  NUMA 與 SWAP

  很多情況下系統(tǒng)剩余內(nèi)存較多,但 SWAP 依舊升高,,這是由于處理器的 NUMA 架構(gòu),。

  在NUMA架構(gòu)下多個(gè)處理器劃分到不同的 Node,每個(gè)Node都擁有自己的本地內(nèi)存空間,。在分析內(nèi)存的使用時(shí)應(yīng)該針對每個(gè)Node單獨(dú)分析

  微信截圖_20221129142714.png

  內(nèi)存三個(gè)閾值可以通過 /proc/zoneinfo 來查看,,該文件中還包括活躍和非活躍的匿名頁/文件頁數(shù)。

  當(dāng)某個(gè)Node內(nèi)存不足時(shí),,系統(tǒng)可以從其他Node尋找空閑資源,,也可以從本地內(nèi)存中回收內(nèi)存。通過

  /proc/sys/vm/zone_raclaim_mode來調(diào)整,。

  0表示既可以從其他Node尋找空閑資源,,也可以從本地回收內(nèi)存

  1,2,,4 表示只回收本地內(nèi)存,,2表示可以會回臟數(shù)據(jù)回收內(nèi)存,4表示可以用Swap方式回收內(nèi)存,。

  swappiness

  在實(shí)際回收過程中Linux根據(jù) /proc/sys/vm/swapiness 選項(xiàng)來調(diào)整使用Swap的積極程度,,從 0-100,數(shù)值越大越積極使用 Swap,,即更傾向于回收匿名頁;數(shù)值越小越消極使用 Swap,即更傾向于回收文件頁,。

  注意:這只是調(diào)整 Swap 積極程度的權(quán)重,,即使設(shè)置為0,當(dāng)剩余內(nèi)存+文件頁小于頁高閾值時(shí),,還是會發(fā)生 Swap,。

  Swap升高時(shí)如何定位分析

 微信截圖_20221129142741.png

  說明剩余內(nèi)存和緩沖區(qū)的波動變化正是由于內(nèi)存回收和緩存再次分配的循環(huán)往復(fù)。有時(shí)候 Swap 用的多,,有時(shí)候緩沖區(qū)波動更多,。此時(shí)查看 swappiness 值為60,是一個(gè)相對中和的配置,,系統(tǒng)會根據(jù)實(shí)際運(yùn)行情況來選去合適的回收類型,。

  如何“快準(zhǔn)狠”找到系統(tǒng)內(nèi)存存在的問題

  內(nèi)存性能指標(biāo)

  系統(tǒng)內(nèi)存指標(biāo)

  已用內(nèi)存/剩余內(nèi)存

  共享內(nèi)存 (tmpfs實(shí)現(xiàn))

  可用內(nèi)存:包括剩余內(nèi)存和可回收內(nèi)存

  緩存:磁盤讀取文件的頁緩存,slab分配器中的可回收部分

  緩沖區(qū):原始磁盤塊的臨時(shí)存儲,,緩存將要寫入磁盤的數(shù)據(jù)

  進(jìn)程內(nèi)存指標(biāo)

  虛擬內(nèi)存:5大部分

  常駐內(nèi)存:進(jìn)程實(shí)際使用的物理內(nèi)存,,不包括Swap和共享內(nèi)存

  共享內(nèi)存:與其他進(jìn)程共享的內(nèi)存,以及動態(tài)鏈接庫和程序的代碼段

  Swap 內(nèi)存:通過Swap換出到磁盤的內(nèi)存

  缺頁異常

  可以直接從物理內(nèi)存中分配,,次缺頁異常

  需要磁盤 IO 介入(如 Swap),,主缺頁異常。此時(shí)內(nèi)存訪問會慢很多

  內(nèi)存性能工具

  根據(jù)不同的性能指標(biāo)來找合適的工具:

  微信截圖_20221129142824.png

  內(nèi)存分析工具包含的性能指標(biāo):

  微信截圖_20221129142839.png

  如何迅速分析內(nèi)存的性能瓶頸

  通常先運(yùn)行幾個(gè)覆蓋面比較大的性能工具,,如 free,,top,vmstat,,pidstat 等

  先用 free 和 top 查看系統(tǒng)整體內(nèi)存使用情況

  再用 vmstat 和 pidstat,,查看一段時(shí)間的趨勢,從而判斷內(nèi)存問題的類型

  最后進(jìn)行詳細(xì)分析,,比如內(nèi)存分配分析,,緩存/緩沖區(qū)分析,具體進(jìn)程的內(nèi)存使用分析等

  常見的優(yōu)化思路:

  最好禁止 Swap,,若必須開啟則盡量降低 swappiness 的值

  減少內(nèi)存的動態(tài)分配,,如可以用內(nèi)存池,HugePage 等

  盡量使用緩存和緩沖區(qū)來訪問數(shù)據(jù),。如用堆棧明確聲明內(nèi)存空間來存儲需要緩存的數(shù)據(jù),,或者用 Redis 外部緩存組件來優(yōu)化數(shù)據(jù)的訪問

  cgroups 等方式來限制進(jìn)程的內(nèi)存使用情況,確保系統(tǒng)內(nèi)存不被異常進(jìn)程耗盡

  /proc/pid/oom_adj 調(diào)整核心應(yīng)用的 oom_score,,保證即使內(nèi)存緊張核心應(yīng)用也不會被OOM殺死

  vmstat 使用詳解

  vmstat 命令是最常見的 Linux/Unix 監(jiān)控工具,,可以展現(xiàn)給定時(shí)間間隔的服務(wù)器的狀態(tài)值,包括服務(wù)器的 CPU 使用率,,內(nèi)存使用,,虛擬內(nèi)存交換情況,,IO讀寫情況??梢钥吹秸麄€(gè)機(jī)器的 CPU,,內(nèi)存,IO 的使用情況,,而不是單單看到各個(gè)進(jìn)程的 CPU 使用率和內(nèi)存使用率(使用場景不一樣),。

 微信截圖_20221129142916.png微信截圖_20221129142936.png微信截圖_20221129142956.png微信截圖_20221129143020.png微信截圖_20221129143038.png

 

  pidstat 使用詳解

  pidstat 主要用于監(jiān)控全部或指定進(jìn)程占用系統(tǒng)資源的情況,如CPU,,內(nèi)存,、設(shè)備IO、任務(wù)切換,、線程等,。

  使用方法:

  pidstat –d interval times 統(tǒng)計(jì)各個(gè)進(jìn)程的IO使用情況

  pidstat –u interval times 統(tǒng)計(jì)各個(gè)進(jìn)程的CPU統(tǒng)計(jì)信息

  pidstat –r interval times 統(tǒng)計(jì)各個(gè)進(jìn)程的內(nèi)存使用信息

  pidstat -w interval times 統(tǒng)計(jì)各個(gè)進(jìn)程的上下文切換

  p PID 指定PID

  1、統(tǒng)計(jì) IO 使用情況

 微信截圖_20221129143116.png    微信截圖_20221129143142.png

  UID

  PID

  kB_rd/s:每秒進(jìn)程從磁盤讀取的數(shù)據(jù)量 KB 單位 read from disk each second KB

  kB_wr/s:每秒進(jìn)程向磁盤寫的數(shù)據(jù)量 KB 單位 write to disk each second KB

  kB_ccwr/s:每秒進(jìn)程向磁盤寫入,,但是被取消的數(shù)據(jù)量,,This may occur when the task truncates some dirty pagecache.

  iodelay:Block I/O delay,measured in clock ticks

  Command:進(jìn)程名 task name

  2,、統(tǒng)計(jì) CPU 使用情況

微信截圖_20221129143212.png

  UID

  PID

  %usr: 進(jìn)程在用戶空間占用 cpu 的百分比

  %system: 進(jìn)程在內(nèi)核空間占用 CPU 百分比

  %guest: 進(jìn)程在虛擬機(jī)占用 CPU 百分比

  %wait: 進(jìn)程等待運(yùn)行的百分比

  %CPU: 進(jìn)程占用 CPU 百分比

  CPU: 處理進(jìn)程的 CPU 編號

  Command: 進(jìn)程名

  3,、統(tǒng)計(jì)內(nèi)存使用情況

  微信截圖_20221129143245.png      微信截圖_20221129143303.png

  UID

  PID

  Minflt/s : 每秒次缺頁錯(cuò)誤次數(shù) (minor page faults),虛擬內(nèi)存地址映射成物理內(nèi)存地址產(chǎn)生的 page fault 次數(shù)

  Majflt/s : 每秒主缺頁錯(cuò)誤次數(shù) (major page faults), 虛擬內(nèi)存地址映射成物理內(nèi)存地址時(shí),,相應(yīng) page 在 swap 中

  VSZ virtual memory usage : 該進(jìn)程使用的虛擬內(nèi)存 KB 單位

  RSS : 該進(jìn)程使用的物理內(nèi)存 KB 單位

  %MEM : 內(nèi)存使用率

  Command : 該進(jìn)程的命令 task name

  4,、查看具體進(jìn)程使用情況

 微信截圖_20221129143330.png



本站內(nèi)容除特別聲明的原創(chuàng)文章之外,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,,并不代表本網(wǎng)站贊同其觀點(diǎn),。轉(zhuǎn)載的所有的文章、圖片,、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有,。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者。如涉及作品內(nèi)容,、版權(quán)和其它問題,,請及時(shí)通過電子郵件或電話通知我們,以便迅速采取適當(dāng)措施,,避免給雙方造成不必要的經(jīng)濟(jì)損失,。聯(lián)系電話:010-82306118;郵箱:[email protected],。