10分鐘后性能測(cè)試瓶頸調(diào)優(yōu)!想進(jìn)大廠這個(gè)必須會(huì)

目錄

  • 引言:性能瓶頸調(diào)優(yōu)

  • 性能調(diào)優(yōu)步驟

  • 性能瓶頸概率分布

  • 系統(tǒng)資源

  • CPU

  • top 參數(shù)詳解

  • 性能分析思路

  • 案例分析

  • LOAD

  • 內(nèi)存

  • top 參數(shù)詳解

  • free 參數(shù)詳解

  • 磁盤 I/O

  • 網(wǎng)絡(luò)

  • 網(wǎng)絡(luò)流量監(jiān)控

  • 網(wǎng)絡(luò)連接狀態(tài)監(jiān)控

  • 數(shù)據(jù)庫(kù)

  • 慢查詢

  • 連接數(shù)

  • 緩存命中率

  • 案例分析

  • JAVA 應(yīng)用

  • JVM

  • 垃圾回收機(jī)制

引言:性能瓶頸調(diào)優(yōu)

在實(shí)際的性能測(cè)試中,會(huì)遇到各種各樣的問(wèn)題,比如 TPS 壓不上去等,導(dǎo)致這種現(xiàn)象的原因有很多,測(cè)試人員應(yīng)配合開(kāi)發(fā)人員進(jìn)行分析,盡快找出瓶頸所在。

性能調(diào)優(yōu)步驟

  1. 確定問(wèn)題:根據(jù)性能監(jiān)控的數(shù)據(jù)和性能分析的結(jié)果,確定性能存在的問(wèn)題。
  2. 確定原因:確定問(wèn)題之后,對(duì)問(wèn)題進(jìn)行分析,找出問(wèn)題的原因。
  3. 確定解決方案(改服務(wù)器參數(shù)配置/增加硬件資源配置/修改代碼)。
  4. 驗(yàn)證解決方案,分析調(diào)優(yōu)結(jié)果。

注意:性能測(cè)試調(diào)優(yōu)并不是一次完成的過(guò)程,針對(duì)同一個(gè)性能問(wèn)題,上述步驟可能要經(jīng)過(guò)多次循環(huán)才能最終完成性能調(diào)優(yōu)的目標(biāo),即:測(cè)試發(fā)現(xiàn)問(wèn)題 -> 找原因 -> 調(diào)整 -> 驗(yàn)證 -> 分析 -> 再測(cè)試 ...

性能瓶頸概率分布

60%:數(shù)據(jù)庫(kù)瓶頸

  • 數(shù)據(jù)庫(kù)服務(wù)器 CPU 使用率高(慢查詢、SQL 過(guò)多、連接數(shù)過(guò)多)
  • 拋出連接數(shù)過(guò)多(連接池設(shè)置太小,導(dǎo)致連接排隊(duì))
  • 數(shù)據(jù)庫(kù)出現(xiàn)死鎖

25%:應(yīng)用瓶頸

  • 應(yīng)用出現(xiàn)內(nèi)存泄露
  • 應(yīng)用出現(xiàn)線程競(jìng)爭(zhēng)/死鎖
  • 程序代碼的算法復(fù)雜度
  • 中間件、第三方應(yīng)用出現(xiàn)異常
  • 計(jì)算密集型任務(wù)引起 CPU 負(fù)載高
  • I/O 密集型任務(wù)引起 I/O 負(fù)載高

10%:壓測(cè)工具瓶頸

  • JMeter 單機(jī)負(fù)載能力有限,如果需要模擬的用戶請(qǐng)求數(shù)超過(guò)其負(fù)載極限,也會(huì)導(dǎo)致 TPS 壓不上去

5%:Linux 機(jī)器出現(xiàn)異常

  • Linux 可用內(nèi)存無(wú)法回收(開(kāi)銷速率大于回收速率)

系統(tǒng)資源

  • CPU監(jiān)控內(nèi)容:CPU 使用率、CPU 使用類型(用戶進(jìn)程、內(nèi)核進(jìn)程)瓶頸分析:CPU已壓滿(接近 100%),需要再看其他指標(biāo)的拐點(diǎn)所出現(xiàn)的時(shí)刻是否與 CPU 壓滿的時(shí)刻基本一致。
  • 內(nèi)存監(jiān)控內(nèi)容:實(shí)際內(nèi)存、虛擬內(nèi)存瓶頸分析:內(nèi)存不足時(shí),操作系統(tǒng)會(huì)使用虛擬內(nèi)存,從虛擬內(nèi)存讀取數(shù)據(jù),影響處理速度。
  • 磁盤 I/O監(jiān)控內(nèi)容:I/O 速度、磁盤等待隊(duì)列瓶頸分析:磁盤 I/O 成為瓶頸時(shí),會(huì)出現(xiàn)磁盤I/O繁忙,導(dǎo)致交易執(zhí)行時(shí)在 I/O 處等待。
  • 網(wǎng)絡(luò)監(jiān)控內(nèi)容:網(wǎng)絡(luò)流量(帶寬使用率)、網(wǎng)絡(luò)連接狀態(tài)瓶頸分析:如果接口傳遞的數(shù)據(jù)包過(guò)大,超過(guò)了帶寬的傳輸能力,就會(huì)造成網(wǎng)絡(luò)資源競(jìng)爭(zhēng), 導(dǎo)致 TPS 上不去。

發(fā)現(xiàn)了瓶頸后,只要對(duì)癥下藥就可以了。簡(jiǎn)單來(lái)說(shuō)無(wú)論哪個(gè)地方出現(xiàn)瓶頸,只需要降低壓力或者增加這部分瓶頸資源(應(yīng)用軟件沒(méi)有瓶頸或優(yōu)化空間之后),即可緩解癥狀。

  • CPU 瓶頸:增加 CPU 資源。
  • 內(nèi)存瓶頸:增加內(nèi)存、釋放緩存。
  • 磁盤 I/O 瓶頸:更換性能更高的磁盤(如固態(tài) SSD)。
  • 網(wǎng)絡(luò)帶寬瓶頸;增加網(wǎng)絡(luò)帶寬。

CPU

后臺(tái)服務(wù)的所有指令和數(shù)據(jù)處理都是由 CPU 負(fù)責(zé),服務(wù)對(duì) CPU 的利用率對(duì)服務(wù)的性能起著決定性的作用。

top 參數(shù)詳解

下面以 top 命令的輸出例,對(duì) CPU 各項(xiàng)主要指標(biāo)進(jìn)行說(shuō)明:

  • us(user):運(yùn)行(未調(diào)整優(yōu)先級(jí)的)用戶進(jìn)程所消耗的 CPU 時(shí)間的百分比。像 shell 程序、各種語(yǔ)言的編譯器、數(shù)據(jù)庫(kù)應(yīng)用、web 服務(wù)器和各種桌面應(yīng)用都算是運(yùn)行在用戶地址空間的進(jìn)程。這些程序如果不是處于 idle 狀態(tài),那么絕大多數(shù)的 CPU 時(shí)間都是運(yùn)行在用戶態(tài)。
  • sy(system):運(yùn)行內(nèi)核進(jìn)程所消耗的 CPU 時(shí)間的百分比。所有進(jìn)程要使用的系統(tǒng)資源都是由 Linux 內(nèi)核處理的。當(dāng)處于用戶態(tài)(用戶地址空間)的進(jìn)程需要使用系統(tǒng)的資源時(shí),比如需要分配一些內(nèi)存、或是執(zhí)行 I/O 操作、再或者是去創(chuàng)建一個(gè)子進(jìn)程,此時(shí)就會(huì)進(jìn)入內(nèi)核態(tài)(內(nèi)核地址空間)運(yùn)行。事實(shí)上,決定進(jìn)程在下一時(shí)刻是否會(huì)被運(yùn)行的進(jìn)程調(diào)度程序就運(yùn)行在內(nèi)核態(tài)。對(duì)于操作系統(tǒng)的設(shè)計(jì)來(lái)說(shuō),消耗在內(nèi)核態(tài)的時(shí)間應(yīng)該是越少越好。通常 sy 比例過(guò)高意味著被測(cè)服務(wù)在用戶態(tài)和系統(tǒng)態(tài)之間切換比較頻繁,此時(shí)系統(tǒng)整體性能會(huì)有一定下降。在實(shí)踐中有一類典型的情況會(huì)使 sy 變大,那就是大量的 I/O 操作,因此在調(diào)查 I/O 相關(guān)的問(wèn)題時(shí)需要著重關(guān)注它。大部分后臺(tái)服務(wù)使用的 CPU 時(shí)間片中 us 和 sy 的占用比例是最高的。同時(shí)這兩個(gè)指標(biāo)又是互相影響的,us 的比例高了,sy 的比例就低,反之亦然。另外,在使用多核 CPU 的服務(wù)器上,CPU 0 負(fù)責(zé) CPU 各核間的調(diào)度,CPU 0 上的使用率過(guò)高會(huì)導(dǎo)致其他 CPU 核心之間的調(diào)度效率變低。因此測(cè)試過(guò)程中需要重點(diǎn)關(guān)注 CPU 0。
  • ni(niced):用做 nice 加權(quán)的進(jìn)程分配的用戶態(tài) CPU 時(shí)間百分比。每個(gè) Linux 進(jìn)程都有個(gè)優(yōu)先級(jí),優(yōu)先級(jí)高的進(jìn)程有優(yōu)先執(zhí)行的權(quán)利,這個(gè)叫做 pri。進(jìn)程除了優(yōu)先級(jí)外,還有個(gè)優(yōu)先級(jí)的修正值。這個(gè)修正值就叫做進(jìn)程的 nice 值。這里顯示的 ni 表示調(diào)整過(guò) nice 值的進(jìn)程消耗掉的 CPU 時(shí)間。如果系統(tǒng)中沒(méi)有進(jìn)程被調(diào)整過(guò) nice 值,那么 ni 就顯示為 0。一般來(lái)說(shuō),被測(cè)服務(wù)和服務(wù)器整體的 ni 值不會(huì)很高。如果測(cè)試過(guò)程中 ni 的值比較高,需要從服務(wù)器 Linux 系統(tǒng)配置、被測(cè)服務(wù)運(yùn)行參數(shù)查找原因。
  • id(idle):空閑的 CPU 時(shí)間百分比。一般情況下, us + ni + id 應(yīng)該接近 100%。線上服務(wù)運(yùn)行過(guò)程中,需要保留一定的 id 冗余來(lái)應(yīng)對(duì)突發(fā)的流量激增。在性能測(cè)試過(guò)程中,如果 id 一直很低,吞吐量上不去,需要檢查被測(cè)服務(wù)線程/進(jìn)程配置、服務(wù)器系統(tǒng)配置等。
  • wa(I/O wait):CPU 等待 I/O 完成時(shí)間百分比。和 CPU 的處理速度相比,磁盤 I/O 操作是非常慢的。有很多這樣的操作,比如:CPU 在啟動(dòng)一個(gè)磁盤讀寫操作后,需要等待磁盤讀寫操作的結(jié)果。在磁盤讀寫操作完成前,CPU 只能處于空閑狀態(tài)。Linux 系統(tǒng)在計(jì)算系統(tǒng)平均負(fù)載時(shí)會(huì)把 CPU 等待 I/O 操作的時(shí)間也計(jì)算進(jìn)去,所以在我們看到系統(tǒng)平均負(fù)載過(guò)高時(shí),可以通過(guò) wa 來(lái)判斷系統(tǒng)的性能瓶頸是不是過(guò)多的 I/O 操作造成的。磁盤、網(wǎng)絡(luò)等 I/O 操作會(huì)導(dǎo)致 CPU 的 wa 指標(biāo)提高。通常情況下,網(wǎng)絡(luò) I/O 占用的 wa 資源不會(huì)很高,而頻繁的磁盤讀寫會(huì)導(dǎo)致 wa 激增。如果被測(cè)服務(wù)不是 I/O 密集型的服務(wù),那需要檢查被測(cè)服務(wù)的日志量、數(shù)據(jù)載入頻率等。如果 wa 高于 10% 則系統(tǒng)開(kāi)始出現(xiàn)卡頓;若高于 20% 則系統(tǒng)幾乎動(dòng)不了;若高于 50% 則很可能磁盤出現(xiàn)故障。
  • hi:硬中斷消耗時(shí)間百分比。
  • si:軟中斷消耗時(shí)間百分比。硬中斷是外設(shè)對(duì) CPU 的中斷,即外圍硬件發(fā)給 CPU 或者內(nèi)存的異步信號(hào)就是硬中斷信號(hào);軟中斷由軟件本身發(fā)給操作系統(tǒng)內(nèi)核的中斷信號(hào)。通常是由硬中斷處理程序或進(jìn)程調(diào)度程序?qū)Σ僮飨到y(tǒng)內(nèi)核的中斷,也就是我們常說(shuō)的系統(tǒng)調(diào)用(System Call)。在性能測(cè)試過(guò)程中,hi 會(huì)有一定的 CPU 占用率,但不會(huì)太高。對(duì)于 I/O 密集型的服務(wù),si 的 CPU 占用率會(huì)高一些。
  • st:虛擬機(jī)等待 CPU 資源的時(shí)間。只有 Linux 在作為虛擬機(jī)運(yùn)行時(shí) st 才是有意義的。它表示虛機(jī)等待 CPU 資源的時(shí)間(虛機(jī)分到的是虛擬 CPU,當(dāng)需要真實(shí)的 CPU 時(shí),可能真實(shí)的 CPU 正在運(yùn)行其它虛機(jī)的任務(wù),所以需要等待)。

性能分析思路

  • wa(IO wait)的值過(guò)高,表示硬盤存在 I/O 瓶頸。
  • id(idle)值高,表示 CPU 較空閑。
  • 如果 id 值高但系統(tǒng)響應(yīng)慢時(shí),有可能是 CPU 等待分配內(nèi)存,此時(shí)應(yīng)加大內(nèi)存容量。
  • 如果 id 值持續(xù)低于 10,那么系統(tǒng)的 CPU 處理能力相對(duì)較低,表明系統(tǒng)中最需要解決的資源是 CPU。

案例分析

現(xiàn)象:CPU 的 us 和 sy 不高,但 wa 很高。

如果被測(cè)服務(wù)是磁盤 I/O 密集型服務(wù),wa 高屬于正?,F(xiàn)象。但如果不是此類服務(wù),最可能導(dǎo)致 wa 高的原因有兩個(gè):

  • 服務(wù)對(duì)磁盤讀寫的業(yè)務(wù)邏輯有問(wèn)題,讀寫頻率過(guò)高,寫入數(shù)據(jù)量過(guò)大,如不合理的數(shù)據(jù)載入策略、log 過(guò)多等,都有可能導(dǎo)致這種問(wèn)題。
  • 服務(wù)器內(nèi)存不足,服務(wù)在 swap 分區(qū)不停的換入換出。

LOAD

Linux 的系統(tǒng)負(fù)載指在特定時(shí)間間隔內(nèi)(一個(gè) CPU 周期)運(yùn)行隊(duì)列中的平均進(jìn)程數(shù)。

(注意:Linux 中的 Load 體現(xiàn)的是整體系統(tǒng)負(fù)載,即 CPU 負(fù)載 + 磁盤負(fù)載 + 網(wǎng)絡(luò)負(fù)載 + 其余外設(shè)負(fù)載,并不能完全等同于 CPU 使用率。而在其余系統(tǒng)如 Unix,Load 還是只代表 CPU 復(fù)雜。)

從服務(wù)器負(fù)載的定義可以看出,服務(wù)器運(yùn)行最理想的狀態(tài)是所有 CPU 核心的運(yùn)行隊(duì)列都為 1,即所有活動(dòng)進(jìn)程都在運(yùn)行,沒(méi)有等待。這種狀態(tài)下服務(wù)器運(yùn)行在負(fù)載閾值下。

通常情況下,按照經(jīng)驗(yàn)值,服務(wù)器的負(fù)載應(yīng)位于閾值的 70%~80%,這樣既能利用服務(wù)器大部分性能,又留有一定的性能冗余應(yīng)對(duì)流量增長(zhǎng)。

查看系統(tǒng)負(fù)載閾值的命令如下:


Linux 提供了很多查看系統(tǒng)負(fù)載的命令,最常用的是 top 和 uptime

top 和 uptime 針對(duì)負(fù)載的輸出內(nèi)容相同,都是系統(tǒng)最近 1 分鐘、5 分鐘、15 分鐘的負(fù)載均值

這三個(gè)數(shù)值的使用方法和 CPU 核數(shù)相關(guān),首先確認(rèn) CPU 物理總核數(shù):

  • /proc/cpuinfo 中的 processors 的最大值不一定是 CPU 的核數(shù),有可能該 CPU 支持超線程技術(shù),從而 processors 是物理核數(shù)的 2 倍。
  • 這里我們需要準(zhǔn)確的核數(shù),具體方法為:找到 /proc/cpuinfo 文件中所有的 physical id 后的數(shù)值,取得最大的數(shù)值,加一后就是實(shí)際的 CPU 個(gè)數(shù)。然后查找任意一個(gè) processors 下的 cpu cores,即是該顆 CPU 的核數(shù),實(shí)際 CPU 個(gè)數(shù)乘以核數(shù)即為 CPU 的物理總核數(shù)。

示例:

[root@localhost home]# cat /proc/cpuinfo |grep "physical id"
physical id     : 0
physical id     : 0
[root@localhost home]# cat /proc/cpuinfo |grep "cpu cores"
cpu cores       : 2
cpu cores       : 2

物理 CPU 個(gè)數(shù)為 0+1=1 個(gè),每個(gè) CPU 的核數(shù)為 2 個(gè),所以總的物理核數(shù)為 2x1=2。

計(jì)算結(jié)果說(shuō)明該機(jī)器的在單位時(shí)間內(nèi)可以處理的進(jìn)程數(shù)是 2 個(gè),如果單位時(shí)間內(nèi)進(jìn)程數(shù)超過(guò) 2 個(gè),就會(huì)出現(xiàn)擁堵的情況,load 就會(huì)持續(xù)增高,增高到一定程度,就會(huì)出現(xiàn)系統(tǒng)崩潰等異常情況。


在性能測(cè)試過(guò)程中,系統(tǒng)負(fù)載是評(píng)價(jià)整個(gè)系統(tǒng)運(yùn)行狀況最重要的指標(biāo)之一。通常情況下:

  • 負(fù)載測(cè)試時(shí):系統(tǒng)負(fù)載應(yīng)接近但不能超過(guò)閾值。
  • 并發(fā)測(cè)試時(shí):系統(tǒng)負(fù)載最高不能超過(guò)閾值的 80%。
  • 穩(wěn)定性測(cè)試時(shí):系統(tǒng)負(fù)載應(yīng)在閾值的 50% 左右。

機(jī)器針對(duì)突發(fā)情況的處理

  • 如果 1 分鐘 load 很高,5 分鐘 load 較高,15 分鐘 load 起伏不大的情況下,說(shuō)明該次高 load 為突發(fā)情況,可以容忍。
  • 如果高 load 持續(xù),導(dǎo)致 5 分鐘和 15 分鐘 load 都已經(jīng)超過(guò)報(bào)警值,這時(shí)候需要考慮進(jìn)行處理。
  • 如果 15 分鐘 load 高于 1 分鐘 load,說(shuō)明高 load 情況已經(jīng)得到緩解。

內(nèi)存

性能測(cè)試過(guò)程中對(duì)內(nèi)存監(jiān)控的主要目的是檢查被測(cè)服務(wù)所占用內(nèi)存的波動(dòng)情況。

top 參數(shù)詳解

在 Linux 系統(tǒng)中有多個(gè)命令可以獲取指定進(jìn)程的內(nèi)存使用情況,最常用的是 top 命令,如下圖所示:

  • VIRT:進(jìn)程所使用的虛擬內(nèi)存的總數(shù)。它包括所有的代碼,數(shù)據(jù)和共享庫(kù),加上已換出的頁(yè)面,所有已申請(qǐng)的總內(nèi)存空間。
  • RES:進(jìn)程正在使用的沒(méi)有交換的物理內(nèi)存(棧、堆)。申請(qǐng)內(nèi)存后該內(nèi)存段已被重新賦值。
  • SHR:進(jìn)程使用共享內(nèi)存的總數(shù)。該數(shù)值只是反映可能與其它進(jìn)程共享的內(nèi)存,不代表這段內(nèi)存當(dāng)前正被其他進(jìn)程使用。
  • SWAP:進(jìn)程使用的虛擬內(nèi)存中被換出的大小。交換的是已經(jīng)申請(qǐng)但沒(méi)有使用的空間(包括棧、堆、共享內(nèi)存)。
  • DATA:進(jìn)程除可執(zhí)行代碼以外的物理內(nèi)存總量,即進(jìn)程棧、堆申請(qǐng)的總空間。

從上面的解釋可以看出,測(cè)試過(guò)程中主要監(jiān)控 RES 和 VIRT,對(duì)于使用了共享內(nèi)存的多進(jìn)程架構(gòu)服務(wù),還需要監(jiān)控 SHR。

free 參數(shù)詳解

free 命令顯示系統(tǒng)內(nèi)存的使用情況,包括物理內(nèi)存、交換內(nèi)存(swap)和內(nèi)核緩沖區(qū)內(nèi)存。如果加上 -h 選項(xiàng)(控制顯示單位),輸出的結(jié)果會(huì)友好很多:

有時(shí)我們需要持續(xù)的觀察內(nèi)存的狀況,此時(shí)可以使用 -s 選項(xiàng)并指定間隔的秒數(shù):如 free -h -s 3 表示每隔 3 秒輸出一次內(nèi)存的使用情況,直到按下 ctrl + c。

  • Mem 行:物理內(nèi)存的使用情況。
  • Swap 行:交換空間的使用情況。swap space 是磁盤上的一塊區(qū)域,可以是一個(gè)分區(qū),也可以是一個(gè)文件,所以具體的實(shí)現(xiàn)可以是 swap 分區(qū)也可以是 swap 文件。當(dāng)系統(tǒng)物理內(nèi)存吃緊時(shí),Linux 會(huì)將內(nèi)存中不常訪問(wèn)的數(shù)據(jù)保存到 swap 上,這樣系統(tǒng)就有更多的物理內(nèi)存為各個(gè)進(jìn)程服務(wù),而當(dāng)系統(tǒng)需要訪問(wèn) swap 上存儲(chǔ)的內(nèi)容時(shí),再將 swap 上的數(shù)據(jù)加載到內(nèi)存中,這就是常說(shuō)的換出和換入。交換空間可以在一定程度上緩解內(nèi)存不足的情況,但是它需要讀寫磁盤數(shù)據(jù),所以性能不是很高。因此當(dāng)交換空間內(nèi)存開(kāi)始使用,則表明內(nèi)存嚴(yán)重不足。如果系統(tǒng)內(nèi)存充足或是做性能壓測(cè)的機(jī)器,可以使用 swapoff -a 關(guān)閉交換空間,或在 /etc/sysctl.conf 文件中設(shè)置 swappiness 值。如果系統(tǒng)內(nèi)存不富余,則需要根據(jù)物理內(nèi)存的大小來(lái)設(shè)置交換空間的大小,具體的策略網(wǎng)上有很豐富的資料。
  • total 列:系統(tǒng)總的可用物理內(nèi)存和交換空間大小。
  • used 列:已經(jīng)被使用的物理內(nèi)存和交換空間大小。
  • free 列:還有多少物理內(nèi)存和交換空間可用使用(真正尚未被使用的物理內(nèi)存數(shù)量)。在吞吐量固定的前提下,如果內(nèi)存持續(xù)上漲,那么很有可能是被測(cè)服務(wù)存在明顯的內(nèi)存泄漏,需要使用 valgrind 等內(nèi)存檢查工具進(jìn)行定位。
  • shared 列:被共享使用的物理內(nèi)存大小。
  • buffer/cache 列:被 buffer 和 cache 使用了的物理內(nèi)存大小。Linux 內(nèi)核為了提升磁盤操作的性能,會(huì)消耗一部分空閑內(nèi)存去緩存磁盤數(shù)據(jù),就是 buffer 和 cache。如果給所有應(yīng)用分配足夠內(nèi)存后,物理內(nèi)存還有剩余,linux 會(huì)盡量再利用這些空閑內(nèi)存,以提高整體 I/O 效率,其方法是把這部分剩余內(nèi)存再劃分為 cache 及 buffer 兩部分加以利用。
  • 所以,空閑物理內(nèi)存不多,不一定表示系統(tǒng)運(yùn)行狀態(tài)很差,因?yàn)閮?nèi)存的 cache 及 buffer 部分可以隨時(shí)被重用,在某種意義上,這兩部分內(nèi)存也可以看作是額外的空閑內(nèi)存。
  • available 列:還可以被應(yīng)用程序使用的物理內(nèi)存大小。從應(yīng)用程序的角度來(lái)說(shuō),available = free + buffer + cache。請(qǐng)注意,這只是一個(gè)很理想的計(jì)算方式,實(shí)際中的數(shù)據(jù)往往有較大的誤差。

釋放緩存內(nèi)存

方式一:手動(dòng)釋放緩存內(nèi)存

snyc
echo 3 > /proc/sys/vm/drop_caches
free -m

方式二:修改 linux 配置自動(dòng)釋放

/proc/sys/vm/drop_caches 這個(gè)值的 0 改為 1

磁盤 I/O

性能測(cè)試過(guò)程中,如果被測(cè)服務(wù)對(duì)磁盤讀寫過(guò)于頻繁,會(huì)導(dǎo)致大量請(qǐng)求處于 I/O 等待的狀態(tài),系統(tǒng)負(fù)載升高,響應(yīng)時(shí)間變長(zhǎng),吞吐量下降。

iostat 參數(shù)詳解

Linux 下可以用 iostat 命令來(lái)監(jiān)控磁盤狀態(tài)。

iostat -d 2 10 表示每 2 秒統(tǒng)計(jì)一次基礎(chǔ)數(shù)據(jù),統(tǒng)計(jì) 10 次:

  • tps:該設(shè)備每秒的傳輸次數(shù)。“一次傳輸”意思是“一次 I/O 請(qǐng)求”。多個(gè)邏輯請(qǐng)求可能會(huì)被合并為“一次 I/O 請(qǐng)求”?!耙淮蝹鬏敗闭?qǐng)求的大小是未知的。
  • kB_read/s:每秒從設(shè)備(driveexpressed)讀取的數(shù)據(jù)量,單位為 Kilobytes。
  • kB_wrtn/s:每秒向設(shè)備(driveexpressed)寫入的數(shù)據(jù)量,單位為 Kilobytes。
  • kB_read:讀取的總數(shù)據(jù)量,單位為 Kilobytes。
  • kB_wrtn:寫入的總數(shù)量數(shù)據(jù)量,單位為 Kilobytes。

從 iostat -d 的輸出中,能夠獲得系統(tǒng)運(yùn)行最基本的統(tǒng)計(jì)數(shù)據(jù)。但對(duì)于性能測(cè)試來(lái)說(shuō),這些數(shù)據(jù)不能提供更多的信息。需要加上 -x 參數(shù)。

iostat -x 參數(shù)詳解

如 iostat -x 2 10 表示每 2 秒統(tǒng)計(jì)一次更詳細(xì)數(shù)據(jù),統(tǒng)計(jì) 10 次:

  • rrqm/s:每秒這個(gè)設(shè)備相關(guān)的讀取請(qǐng)求有多少被 Merge 了。當(dāng)系統(tǒng)調(diào)用需要讀取數(shù)據(jù)的時(shí)候,VFS 將請(qǐng)求發(fā)到各個(gè) FS,如果 FS 發(fā)現(xiàn)不同的讀取請(qǐng)求讀取的是相同 Block 的數(shù)據(jù),F(xiàn)S 會(huì)將這個(gè)請(qǐng)求合并 Merge。
  • wrqm/s:每秒這個(gè)設(shè)備相關(guān)的寫入請(qǐng)求有多少被 Merge 了。
  • await:每一個(gè) I/O 請(qǐng)求的處理的平均時(shí)間(單位:毫秒)。await 的大小一般取決于服務(wù)時(shí)間(svtcm)以及 I/O 隊(duì)列的長(zhǎng)度和 I/O 請(qǐng)求的發(fā)出模式。假設(shè) svtcm 比較接近 await,說(shuō)明 I/O 差點(diǎn)沒(méi)有等待時(shí)間。假設(shè) await 遠(yuǎn)大于 svctm(如大于 5),就要考慮 I/O 有壓力瓶頸,說(shuō)明 I/O 隊(duì)列太長(zhǎng),應(yīng)用得到的響應(yīng)時(shí)間變慢。假設(shè)響應(yīng)時(shí)間超過(guò)了用戶能夠容許的范圍,這時(shí)可以考慮更換更快的磁盤。
  • svctm:I/O 平均服務(wù)時(shí)間。
  • %util:在統(tǒng)計(jì)時(shí)間內(nèi)有百分之多少用于 I/O 操作。例如,如果統(tǒng)計(jì)間隔 1 秒,該設(shè)備有 0.8 秒在處理 I/O,而 0.2 秒閑置,那么該設(shè)備的 %util = 0.8/1 = 80%,該參數(shù)暗示了設(shè)備的繁忙程度。%util 接近100% 表明 I/O 請(qǐng)求太多,I/O 系統(tǒng)繁忙,磁盤可能存在瓶頸。
iostat -x 完整參數(shù)如下:

- rrqm/s: 每秒進(jìn)行 merge 的讀操作數(shù)目。即 delta(rerge)/s 
- wrqm/s: 每秒進(jìn)行 merge 的寫操作數(shù)目。即 delta(wmerge)/s 
- t/s: 每秒完成的讀 I/O 設(shè)備次數(shù)。即 delta(rioVs 
- w/s: 每秒完成的寫 1/O 設(shè)備次數(shù)。即 delta(wio)/s 
- rsec/s: 每秒讀扇區(qū)數(shù)。即 delta(rsect)/s 
- ws0c/s: 每秒寫扇區(qū)數(shù)。即 deita(wsect)/s 
- rkB/s: 每秒讀 K 字節(jié)數(shù)。是 rsect/s 的一半,因?yàn)槊可葏^(qū)大小為 512 字節(jié)。(需要計(jì)算) 
- wkB/s: 每秒寫 K 字節(jié)數(shù)。是 wsect/s 的一半。(需要計(jì)算) 
- avgrq+sz: 平均每次設(shè)備 I/O 操作的數(shù)據(jù)大小(扇區(qū))。delta(rsect+wsect)/delta(rio+wio) 
- avgqu-sz: 平均I/O隊(duì)列長(zhǎng)度,即delta(avea)/s/1000(因?yàn)?aveq 的單位為毫秒)。 
- await: 平均每次設(shè)備 I/O 操作的等待時(shí)間(毫秒)。即 delta(ruse+wuse)/delta(rio+wio) 
- svctm: 平均每次設(shè)備 I/O 操作的服務(wù)時(shí)間(毫秒)。即 delta(use)/delta(rio+wio) 
- %util:一秒中有百分之多少的時(shí)間用于 I/O 操作,或者說(shuō)一秒中有多少時(shí)間 I/O 隊(duì)列是非空的。即 delta(use)/s/1000(因?yàn)?use 的單位為毫秒)

網(wǎng)絡(luò)

性能測(cè)試中網(wǎng)絡(luò)監(jiān)控主要包括網(wǎng)絡(luò)流量、網(wǎng)絡(luò)連接狀態(tài)的監(jiān)控。

網(wǎng)絡(luò)流量監(jiān)控

方法很多,網(wǎng)上有很多 shell 腳本。也可以使用 nethogs 命令。該命令與 top 類似,是一個(gè)實(shí)時(shí)交互的命令,運(yùn)行界面如下:

在后臺(tái)服務(wù)性能測(cè)試中,對(duì)于返回文本結(jié)果的服務(wù),并不需要太多關(guān)注在流量方面。


理解帶寬

針對(duì)一些特定的應(yīng)用,比如直播或網(wǎng)盤(文件上傳下載),帶寬瓶頸也是一個(gè)出現(xiàn)頻率較高的場(chǎng)景。

服務(wù)端的帶寬分為上行(out)和下行(in)帶寬(分別對(duì)應(yīng)客戶端的下載和上傳)。

  • 看視頻看新聞使用帶寬:客戶端的下載、服務(wù)端的上行帶寬。
  • 服務(wù)端接收客戶端的數(shù)據(jù)使用帶寬:客戶端的上傳、服務(wù)端的下行帶寬。

一個(gè) Web 服務(wù)器如各類新聞網(wǎng)站通常需要更多的服務(wù)端上行(out)帶寬;而郵件服務(wù)器、網(wǎng)盤服務(wù)器等則通常需要更多的服務(wù)端下行帶寬(in)。

理解帶寬速率公式

  • 1 Mb/s 帶寬速度為 128 KB/s(1024Kb / 8KB)
  • 100 Mb/s 帶寬速度為 12.5 Mb/s(考慮網(wǎng)絡(luò)損耗通常按 10M/s 或 1280KB/s 算)

示例:5000 萬(wàn)像素手機(jī)拍一張照片,照片大小約 20MB,在下述帶寬下需要耗時(shí):

  • 10M 帶寬約 20 秒:耗時(shí) = 流量 / 速率 = 20MB / (10Mb/8) = 20 / 1.25 = 16 秒(按 1MB/s=128KB/s 速度算即 20 秒)
  • 100M 帶寬約 2 秒:耗時(shí) = 流量 / 速率 = 20MB / (100Mb/8) = 20 / 12.5 = 1.6 秒(按 10MB/s=128KB/s 速度算即 2 秒)
  • 1000M 帶寬約 0.2 秒:耗時(shí) = 流量 / 速率 = 20MB / (1000Mb/8) = 20 / 125 = 0.16 秒(按 100MB/s=128KB/s 速度算即 0.2 秒)

案例分析

現(xiàn)象:從監(jiān)控圖表可以看出,當(dāng)前的網(wǎng)絡(luò)流量已經(jīng)基本將網(wǎng)絡(luò)帶寬占滿,因此網(wǎng)絡(luò)存在瓶頸。

解決方案:

  • 硬件解決:增加帶寬(帶寬便宜)。
  • 軟件解決:分析對(duì)應(yīng)業(yè)務(wù)操作的數(shù)據(jù)傳送內(nèi)容是否可精簡(jiǎn);是否可以異步傳送。

網(wǎng)絡(luò)連接狀態(tài)監(jiān)控

性能測(cè)試中對(duì)網(wǎng)絡(luò)的監(jiān)控主要是監(jiān)控網(wǎng)絡(luò)連接狀態(tài)的變化和異常。

  • 對(duì)于使用 TCP 協(xié)議的服務(wù),需要監(jiān)控服務(wù)已建立連接的變化情況(即 ESTABLISHED 狀態(tài)的 TCP 連接)。
  • 對(duì)于 HTTP 協(xié)議的服務(wù),需要監(jiān)控被測(cè)服務(wù)對(duì)應(yīng)進(jìn)程的網(wǎng)絡(luò)緩沖區(qū)的狀態(tài)、TIME_WAIT 狀態(tài)的連接數(shù)等。

Linux 自帶的很多命令如 netstat、ss 都支持如上功能。

下圖是 netstat 對(duì)指定 pid 進(jìn)程的監(jiān)控結(jié)果:

完整命令輸出:

數(shù)據(jù)庫(kù)

慢查詢

更具體的慢 SQL 分析優(yōu)化,可參見(jiàn)《MySQL 慢 SQL & 優(yōu)化方案》。

如 MySQL 資源出現(xiàn)瓶頸,首先找慢查詢(超過(guò)自定義的執(zhí)行時(shí)間閾值的 SQL)。

1)通過(guò) SQL 語(yǔ)句定位到慢查詢?nèi)罩镜乃谀夸?,然后查看日志?/p>

show variables like "slow%";

2)慢查詢?nèi)罩驹诓樵兘Y(jié)束以后才紀(jì)錄,所以在應(yīng)用反映執(zhí)行效率出現(xiàn)問(wèn)題時(shí),查詢慢查詢?nèi)罩静⒉荒芏ㄎ粏?wèn)題。這時(shí)可以使用show processlist命令查看當(dāng)前 MySQL 正在進(jìn)行的線程狀態(tài),可以實(shí)時(shí)地查看 SQL 的執(zhí)行情況。

示例:

mysql -uroot -p123456 -h127.0.0.1 -p3307 -e "show full processlist" |grep dbname |grep -v NULL

3)找到慢查詢 SQL 后可以用執(zhí)行計(jì)劃(explain)進(jìn)行分析(或反饋給 DBA 和開(kāi)發(fā)處理)。推薦最簡(jiǎn)單的排查方式,步驟如下:

  1. 分析 SQL 是否加載了不必要的字段/數(shù)據(jù)。
  2. 分析 SQL 是否命中索引。
  3. 如果 SQL 很復(fù)雜,優(yōu)化 SQL 結(jié)構(gòu)。
  4. 如果表數(shù)據(jù)量太大,考慮分表。
  5. ……

連接數(shù)

數(shù)據(jù)庫(kù)連接池的使用率

  • 當(dāng)數(shù)據(jù)庫(kù)連接池被占滿時(shí),如果有新的 SQL 語(yǔ)句要執(zhí)行,只能排隊(duì)等待,等待連接池中的連接被釋放(等待之前的 SQL 語(yǔ)句執(zhí)行完成)。
  • 如果監(jiān)控發(fā)現(xiàn)數(shù)據(jù)庫(kù)連接池的使用率過(guò)高,甚至是經(jīng)常出現(xiàn)排隊(duì)的情況,則需要進(jìn)行調(diào)優(yōu)。

查看/設(shè)置最大連接數(shù)

-- 查看最大連接數(shù)
mysql> show variables like '%max_connection%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| extra_max_connections |       |
| max_connections       | 2512  |
+-----------------------+-------+
2 rows in set (0.00 sec)

-- 重新設(shè)置最大連接數(shù)
set global max_connections=1000;

在/etc/my.cnf 里面設(shè)置數(shù)據(jù)庫(kù)的最大連接數(shù)

[mysqld]
max_connections = 1000

查看當(dāng)前連接數(shù)

mysql> show status like  'Threads%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 32    |
| Threads_connected | 10    |
| Threads_created   | 50    |
| Threads_rejected  | 0     |
| Threads_running   | 1     |
+-------------------+-------+
5 rows in set (0.00 sec)

  • Threads_connected:表示當(dāng)前連接數(shù)。跟 show processlist 結(jié)果相同。準(zhǔn)確的來(lái)說(shuō),Threads_running 代表的是當(dāng)前并發(fā)數(shù)。
  • Threads_running:表示激活的連接數(shù)。一般遠(yuǎn)低于 connected 數(shù)值。
  • Threads_created:表示創(chuàng)建過(guò)的線程數(shù)。如果我們?cè)?MySQL 服務(wù)器配置文件中設(shè)置了 thread_cache_size,那么當(dāng)客戶端斷開(kāi)之后,服務(wù)器處理此客戶的線程將會(huì)緩存起來(lái)以響應(yīng)下一個(gè)客戶而不是銷毀(前提是緩存數(shù)未達(dá)上限)。如果發(fā)現(xiàn) Threads_created 值過(guò)大的話,表明 MySQL 服務(wù)器一直在創(chuàng)建線程,這也是比較耗資源,因此可以適當(dāng)增加配置文件中 thread_cache_size 值。

查詢服務(wù)器 thread_cache_size 的值

mysql> show variables like 'thread_cache_size';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| thread_cache_size | 100   |
+-------------------+-------+
1 row in set (0.00 sec)

詳見(jiàn)《MySQL 事務(wù)和鎖》。

緩存命中率

  1. 通常,SQL 查詢是從磁盤中的數(shù)據(jù)庫(kù)文件中讀取數(shù)據(jù)。
  2. 若當(dāng)某一個(gè) SQL 查詢語(yǔ)句之前執(zhí)行過(guò),則該 SQL 語(yǔ)句及查詢結(jié)果都會(huì)被緩存下來(lái),下次再查詢相同的 SQL 語(yǔ)句時(shí),就會(huì)直接從數(shù)據(jù)庫(kù)緩存中讀取。(注意,MySQL 8 開(kāi)始已廢棄查詢緩存功能。)

監(jiān)控點(diǎn)

  • 業(yè)務(wù)執(zhí)行過(guò)程中 SQL 查詢時(shí)的緩存命中率(查詢語(yǔ)句讀取緩存的次數(shù)占總查詢次數(shù)的比例)。
  • 如果緩存命中率過(guò)低,需要優(yōu)化對(duì)應(yīng)的代碼和 SQL 查詢語(yǔ)句,以提高緩存命中率。

案例分析

測(cè)試結(jié)果分析

結(jié)論:從目前的測(cè)試結(jié)果來(lái)看(如下圖所示),性能存在問(wèn)題。

現(xiàn)象:并發(fā)數(shù)達(dá)到 50 時(shí)的 TPS 為 52,此時(shí)雖然響應(yīng)時(shí)間為 4.4s(小于需求的 5s),但是數(shù)據(jù)庫(kù)服務(wù)器的 CPU 使用率非常高(接近 100%),因此需要重點(diǎn)關(guān)注數(shù)據(jù)庫(kù)的調(diào)優(yōu)分析。

排查過(guò)程

  1. 使用 top 命令觀察,確定是 mysqld 導(dǎo)致還是其他原因。CPU 分為用戶 CPU 和內(nèi)核 CPU。綜合其他的各項(xiàng)資源指標(biāo)來(lái)分析,發(fā)現(xiàn)內(nèi)存、磁盤IO、網(wǎng)絡(luò)等指標(biāo)無(wú)任何異常,因此判斷此處不是內(nèi)核 CPU 占用高,主要原因是用戶進(jìn)程占用的 CPU 高。確認(rèn)目前 CPU 占用高的為 mysqld 進(jìn)程。
  2. 分析數(shù)據(jù)庫(kù)服務(wù)器 CPU 高的可能原因:慢 SQL、SQL 語(yǔ)句過(guò)多、連接數(shù)過(guò)多等。確認(rèn)是否存在慢 SQL查看慢查詢?nèi)罩荆纯词欠裼谐^(guò)預(yù)期指標(biāo)的 SQL 語(yǔ)句,并分析排查:看看執(zhí)行計(jì)劃是否準(zhǔn)確、索引是否缺失、數(shù)據(jù)量是否太大等。目前案例經(jīng)過(guò)慢查詢?nèi)罩镜姆治?,未存在慢查詢。確認(rèn)是否 SQL 語(yǔ)句過(guò)多或連接數(shù)過(guò)多使用show full processlist查看當(dāng)前數(shù)據(jù)庫(kù)中正在執(zhí)行的 SQL 語(yǔ)句及連接池的狀態(tài),發(fā)現(xiàn)大量 SQL 在等待執(zhí)行。再結(jié)合操作過(guò)程中的系統(tǒng)日志進(jìn)行分析,發(fā)現(xiàn)每進(jìn)入一次商城首頁(yè),就需要在數(shù)據(jù)庫(kù)中執(zhí)行 19 條查詢 SQL。

解決方案

  • 硬件解決:增加 CPU。
  • 軟件解決:為減少一次性加載過(guò)多 SQL,可考慮使用分批次、異步加載的方式(展示到什么位置,就查詢什么位置的數(shù)據(jù))。

JAVA 應(yīng)用

JVM

JVM 簡(jiǎn)介

JVM(JAVA Virtual Machine):虛擬出來(lái)的空間,專門供 JAVA 程序運(yùn)行。


JVM 內(nèi)存

重點(diǎn)關(guān)注:堆區(qū)(動(dòng)態(tài)變化)

所有的對(duì)象在初始化都會(huì)申請(qǐng)堆區(qū)的空間,如果已申請(qǐng)的空間在使用結(jié)束后沒(méi)有及時(shí)地釋放,那么該空間就會(huì)被占用,即內(nèi)存泄漏。

監(jiān)控點(diǎn):因此在測(cè)試時(shí),需要關(guān)注堆區(qū)的空間是否持續(xù)上升而沒(méi)有下降。


案例分析

現(xiàn)象:堆內(nèi)存使用是持續(xù)升高,無(wú)法降低到之前的水平。

解決方案:找到內(nèi)存泄漏的代碼,并優(yōu)化代碼。

垃圾回收機(jī)制

什么是垃圾回收機(jī)制

  • 垃圾回收指將內(nèi)存中已申請(qǐng)并使用完成的那部分內(nèi)存空間回收,供新申請(qǐng)使用。
  • 垃圾回收機(jī)制都是針對(duì)堆區(qū)的內(nèi)存進(jìn)行的。

監(jiān)控點(diǎn)

  • 系統(tǒng)在做垃圾回收時(shí),不能夠處理任何用戶業(yè)務(wù)的。如果垃圾回收過(guò)于頻繁,導(dǎo)致系統(tǒng)業(yè)務(wù)處理能力下降。
  • 由于 Full GC 內(nèi)存比較大,垃圾回收一次時(shí)間比較長(zhǎng),那么這段時(shí)間內(nèi)都不能處理業(yè)務(wù),對(duì)系統(tǒng)影響比較大,因此我們需要關(guān)注Full GC 頻率。

垃圾回收機(jī)制的運(yùn)行步驟如下:

  1. 新程序執(zhí)行時(shí)需要先申請(qǐng)內(nèi)存空間,會(huì)先從年輕代中申請(qǐng)。
  2. 在年輕代滿了以后,就會(huì)進(jìn)行垃圾回收Young GC。
  3. 回收時(shí)檢查年輕代中的內(nèi)存,是否還在使用。還在使用的部分會(huì)移存到生存區(qū) 2 中;不使用的部分則釋放,此時(shí)年輕代內(nèi)存空間被清空。
  4. 新程序執(zhí)行申請(qǐng)內(nèi)存空間,再?gòu)哪贻p代申請(qǐng)。
  5. 年輕代又滿了,就會(huì)進(jìn)行垃圾回收Young GC。還在使用的內(nèi)存移存到生存區(qū) 1 中,并把生存區(qū) 2 中的內(nèi)存也都存到生存區(qū) 1 中。此時(shí)就會(huì)清空年輕代和生存區(qū) 2。
  6. 循環(huán)上述 1-5 步。
  7. 如果部分內(nèi)存在生存區(qū)中存活很久(內(nèi)存在生存區(qū)中移動(dòng)了 10 次左右),則將這部分內(nèi)存放入到老年代中。
  8. 循環(huán)上述 1-7 步,直到老年代內(nèi)存空間全部占滿,此時(shí)就要進(jìn)行垃圾回收Full GC。
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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