引言:效能瓶頸調優
在實際的效能測試中,會遇到各種各樣的問題,比如 TPS 壓不上去等,導致這種現象的原因有很多,測試人員應配合開發人員進行分析,儘快找出瓶頸所在。
理想的效能測試指標結果可能不是很高,但一定是平緩的。
效能調優步驟
-
確定問題:根據效能監控的資料和效能分析的結果,確定效能存在的問題。
-
確定原因:確定問題之後,對問題進行分析,找出問題的原因。
-
確定解決方案(改伺服器引數配置/增加硬體資源配置/修改程式碼)。
-
驗證解決方案,分析調優結果。
注意:效能測試調優並不是一次完成的過程,針對同一個效能問題,上述步驟可能要經過多次迴圈才能最終完成效能調優的目標,即:測試發現問題 -> 找原因 -> 調整 -> 驗證 -> 分析 -> 再測試 ...
效能瓶頸概率分佈
60%:資料庫瓶頸
- 資料庫伺服器 CPU 使用率高(慢查詢、SQL 過多、連線數過多)
- 丟擲連線數過多(連線池設定太小,導致連線排隊)
- 資料庫出現死鎖
25%:應用瓶頸
- 應用出現記憶體洩露
- 應用出現執行緒競爭/死鎖
- 程式程式碼的演算法複雜度
- 中介軟體、第三方應用出現異常
- 計算密集型任務引起 CPU 負載高
- I/O 密集型任務引起 I/O 負載高
10%:壓測工具瓶頸
- JMeter 單機負載能力有限,如果需要模擬的使用者請求數超過其負載極限,也會導致 TPS 壓不上去
5%:Linux 機器出現異常
- Linux 可用記憶體無法回收(開銷速率大於回收速率)
系統資源
-
CPU
- 監控內容:CPU 使用率、CPU 使用型別(使用者程式、核心程式)
- 瓶頸分析:CPU已壓滿(接近 100%),需要再看其他指標的拐點所出現的時刻是否與 CPU 壓滿的時刻基本一致。
-
記憶體
- 監控內容:實際記憶體、虛擬記憶體
- 瓶頸分析:記憶體不足時,作業系統會使用虛擬記憶體,從虛擬記憶體讀取資料,影響處理速度。
-
磁碟 I/O
- 監控內容:I/O 速度、磁碟等待佇列
- 瓶頸分析:磁碟 I/O 成為瓶頸時,會出現磁碟I/O繁忙,導致交易執行時在 I/O 處等待。
-
網路
- 監控內容:網路流量(頻寬使用率)、網路連線狀態
- 瓶頸分析:如果介面傳遞的資料包過大,超過了頻寬的傳輸能力,就會造成網路資源競爭, 導致 TPS 上不去。
發現了瓶頸後,只要對症下藥就可以了。簡單來說無論哪個地方出現瓶頸,只需要降低壓力或者增加這部分瓶頸資源(應用軟體沒有瓶頸或優化空間之後),即可緩解症狀。
- CPU 瓶頸:增加 CPU 資源。
- 記憶體瓶頸:增加記憶體、釋放快取。
- 磁碟 I/O 瓶頸:更換效能更高的磁碟(如固態 SSD)。
- 網路頻寬瓶頸;增加網路頻寬。
CPU
後臺服務的所有指令和資料處理都是由 CPU 負責,服務對 CPU 的利用率對服務的效能起著決定性的作用。
top 引數詳解
下面以 top 命令的輸出例,對 CPU 各項主要指標進行說明:
-
us(user):執行(未調整優先順序的)使用者程式所消耗的 CPU 時間的百分比。
-
像 shell 程式、各種語言的編譯器、資料庫應用、web 伺服器和各種桌面應用都算是執行在使用者地址空間的程式。
-
這些程式如果不是處於 idle 狀態,那麼絕大多數的 CPU 時間都是執行在使用者態。
-
-
sy(system):執行核心程式所消耗的 CPU 時間的百分比。
-
所有程式要使用的系統資源都是由 Linux 核心處理的。當處於使用者態(使用者地址空間)的程式需要使用系統的資源時,比如需要分配一些記憶體、或是執行 I/O 操作、再或者是去建立一個子程式,此時就會進入核心態(核心地址空間)執行。事實上,決定程式在下一時刻是否會被執行的程式排程程式就執行在核心態。
-
對於作業系統的設計來說,消耗在核心態的時間應該是越少越好。通常 sy 比例過高意味著被測服務在使用者態和系統態之間切換比較頻繁,此時系統整體效能會有一定下降。
-
在實踐中有一類典型的情況會使 sy 變大,那就是大量的 I/O 操作,因此在調查 I/O 相關的問題時需要著重關注它。
-
大部分後臺服務使用的 CPU 時間片中 us 和 sy 的佔用比例是最高的。同時這兩個指標又是互相影響的,us 的比例高了,sy 的比例就低,反之亦然。
-
另外,在使用多核 CPU 的伺服器上,CPU 0 負責 CPU 各核間的排程,CPU 0 上的使用率過高會導致其他 CPU 核心之間的排程效率變低。因此測試過程中需要重點關注 CPU 0。
-
-
ni(niced):用做 nice 加權的程式分配的使用者態 CPU 時間百分比。
- 每個 Linux 程式都有個優先順序,優先順序高的程式有優先執行的權利,這個叫做 pri。程式除了優先順序外,還有個優先順序的修正值。這個修正值就叫做程式的 nice 值。
- 這裡顯示的 ni 表示調整過 nice 值的程式消耗掉的 CPU 時間。如果系統中沒有程式被調整過 nice 值,那麼 ni 就顯示為 0。
- 一般來說,被測服務和伺服器整體的 ni 值不會很高。如果測試過程中 ni 的值比較高,需要從伺服器 Linux 系統配置、被測服務執行引數查詢原因。
-
id(idle):空閒的 CPU 時間百分比。
-
一般情況下, us + ni + id 應該接近 100%。
-
線上服務執行過程中,需要保留一定的 id 冗餘來應對突發的流量激增。
-
在效能測試過程中,如果 id 一直很低,吞吐量上不去,需要檢查被測服務執行緒/程式配置、伺服器系統配置等。
-
-
wa(I/O wait):CPU 等待 I/O 完成時間百分比。
-
和 CPU 的處理速度相比,磁碟 I/O 操作是非常慢的。有很多這樣的操作,比如:CPU 在啟動一個磁碟讀寫操作後,需要等待磁碟讀寫操作的結果。在磁碟讀寫操作完成前,CPU 只能處於空閒狀態。
-
Linux 系統在計算系統平均負載時會把 CPU 等待 I/O 操作的時間也計算進去,所以在我們看到系統平均負載過高時,可以通過 wa 來判斷系統的效能瓶頸是不是過多的 I/O 操作造成的。
-
磁碟、網路等 I/O 操作會導致 CPU 的 wa 指標提高。通常情況下,網路 I/O 佔用的 wa 資源不會很高,而頻繁的磁碟讀寫會導致 wa 激增。
-
如果被測服務不是 I/O 密集型的服務,那需要檢查被測服務的日誌量、資料載入頻率等。
-
如果 wa 高於 10% 則系統開始出現卡頓;若高於 20% 則系統幾乎動不了;若高於 50% 則很可能磁碟出現故障。
-
-
hi:硬中斷消耗時間百分比。
-
si:軟中斷消耗時間百分比。
- 硬中斷是外設對 CPU 的中斷,即外圍硬體發給 CPU 或者記憶體的非同步訊號就是硬中斷訊號;軟中斷由軟體本身發給作業系統核心的中斷訊號。
- 通常是由硬中斷處理程式或程式排程程式對作業系統核心的中斷,也就是我們常說的系統呼叫(System Call)。
- 在效能測試過程中,hi 會有一定的 CPU 佔用率,但不會太高。對於 I/O 密集型的服務,si 的 CPU 佔用率會高一些。
-
st:虛擬機器等待 CPU 資源的時間。
- 只有 Linux 在作為虛擬機器執行時 st 才是有意義的。它表示虛機等待 CPU 資源的時間(虛機分到的是虛擬 CPU,當需要真實的 CPU 時,可能真實的 CPU 正在執行其它虛機的任務,所以需要等待)。
效能分析思路
-
wa(IO wait)的值過高,表示硬碟存在 I/O 瓶頸。
-
id(idle)值高,表示 CPU 較空閒。
-
如果 id 值高但系統響應慢時,有可能是 CPU 等待分配記憶體,此時應加大記憶體容量。
-
如果 id 值持續低於 10,那麼系統的 CPU 處理能力相對較低,表明系統中最需要解決的資源是 CPU。
案例分析
現象:CPU 的 us 和 sy 不高,但 wa 很高。
如果被測服務是磁碟 I/O 密集型服務,wa 高屬於正常現象。但如果不是此類服務,最可能導致 wa 高的原因有兩個:
-
服務對磁碟讀寫的業務邏輯有問題,讀寫頻率過高,寫入資料量過大,如不合理的資料載入策略、log 過多等,都有可能導致這種問題。
-
伺服器記憶體不足,服務在 swap 分割槽不停的換入換出。
LOAD
Linux 的系統負載指在特定時間間隔內(一個 CPU 週期)執行佇列中的平均程式數。
(注意:Linux 中的 Load 體現的是整體系統負載,即 CPU 負載 + 磁碟負載 + 網路負載 + 其餘外設負載,並不能完全等同於 CPU 使用率。而在其餘系統如 Unix,Load 還是隻代表 CPU 複雜。)
從伺服器負載的定義可以看出,伺服器執行最理想的狀態是所有 CPU 核心的執行佇列都為 1,即所有活動程式都在執行,沒有等待。
這種狀態下伺服器執行在負載閾值下。
通常情況下,按照經驗值,伺服器的負載應位於閾值的 70%~80%,這樣既能利用伺服器大部分效能,又留有一定的效能冗餘應對流量增長。
檢視系統負載閾值的命令如下:
Linux 提供了很多檢視系統負載的命令,最常用的是 top 和 uptime。
top 和 uptime 針對負載的輸出內容相同,都是系統最近 1 分鐘、5 分鐘、15 分鐘的負載均值
:
這三個數值的使用方法和 CPU 核數相關,首先確認 CPU 物理總核數:
-
/proc/cpuinfo 中的 processors 的最大值不一定是 CPU 的核數,有可能該 CPU 支援超執行緒技術,從而 processors 是物理核數的 2 倍。
-
這裡我們需要準確的核數,具體方法為:找到 /proc/cpuinfo 檔案中所有的 physical id 後的數值,取得最大的數值,加一後就是實際的 CPU 個數。然後查詢任意一個 processors 下的 cpu cores,即是該顆 CPU 的核數,實際 CPU 個數乘以核數即為 CPU 的物理總核數。
示例:
[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 個數為 0+1=1 個,每個 CPU 的核數為 2 個,所以總的物理核數為 2x1=2。
計算結果說明該機器的在單位時間內可以處理的程式數是 2 個,如果單位時間內程式數超過 2 個,就會出現擁堵的情況,load 就會持續增高,增高到一定程度,就會出現系統崩潰等異常情況。
在效能測試過程中,系統負載是評價整個系統執行狀況最重要的指標之一。通常情況下:
-
負載測試時:系統負載應接近但不能超過閾值。
-
併發測試時:系統負載最高不能超過閾值的 80%。
-
穩定性測試時:系統負載應在閾值的 50% 左右。
機器針對突發情況的處理
-
如果 1 分鐘 load 很高,5 分鐘 load 較高,15 分鐘 load 起伏不大的情況下,說明該次高 load 為突發情況,可以容忍。
-
如果高 load 持續,導致 5 分鐘和 15 分鐘 load 都已經超過報警值,這時候需要考慮進行處理。
-
如果 15 分鐘 load 高於 1 分鐘 load,說明高 load 情況已經得到緩解。
記憶體
效能測試過程中對記憶體監控的主要目的是檢查被測服務所佔用記憶體的波動情況。
top 引數詳解
在 Linux 系統中有多個命令可以獲取指定程式的記憶體使用情況,最常用的是 top 命令,如下圖所示:
-
VIRT:程式所使用的虛擬記憶體的總數。它包括所有的程式碼,資料和共享庫,加上已換出的頁面,所有已申請的總記憶體空間。
-
RES:程式正在使用的沒有交換的實體記憶體(棧、堆)。申請記憶體後該記憶體段已被重新賦值。
-
SHR:程式使用共享記憶體的總數。該數值只是反映可能與其它程式共享的記憶體,不代表這段記憶體當前正被其他程式使用。
-
SWAP:程式使用的虛擬記憶體中被換出的大小。交換的是已經申請但沒有使用的空間(包括棧、堆、共享記憶體)。
-
DATA:程式除可執行程式碼以外的實體記憶體總量,即程式棧、堆申請的總空間。
從上面的解釋可以看出,測試過程中主要監控 RES 和 VIRT。對於使用了共享記憶體的多程式架構服務,還需要監控 SHR。
free 引數詳解
free 命令顯示系統記憶體的使用情況,包括實體記憶體、交換記憶體(swap)和核心緩衝區記憶體。如果加上 -h 選項(控制顯示單位),輸出的結果會友好很多:
有時我們需要持續的觀察記憶體的狀況,此時可以使用 -s 選項並指定間隔的秒數:如 free -h -s 3 表示每隔 3 秒輸出一次記憶體的使用情況,直到按下 ctrl + c。
-
Mem 行:實體記憶體的使用情況。
-
Swap 行:交換空間的使用情況。
-
swap space 是磁碟上的一塊區域,可以是一個分割槽,也可以是一個檔案,所以具體的實現可以是 swap 分割槽也可以是 swap 檔案。當系統實體記憶體吃緊時,Linux 會將記憶體中不常訪問的資料儲存到 swap 上,這樣系統就有更多的實體記憶體為各個程式服務,而當系統需要訪問 swap 上儲存的內容時,再將 swap 上的資料載入到記憶體中,這就是常說的換出和換入。
-
交換空間可以在一定程度上緩解記憶體不足的情況,但是它需要讀寫磁碟資料,所以效能不是很高。因此
當交換空間記憶體開始使用,則表明記憶體嚴重不足
。 -
如果系統記憶體充足或是做效能壓測的機器,可以使用 swapoff -a 關閉交換空間,或在 /etc/sysctl.conf 檔案中設定 swappiness 值。
如果系統記憶體不富餘,則需要根據實體記憶體的大小來設定交換空間的大小,具體的策略網上有很豐富的資料。
-
-
total 列:系統總的可用實體記憶體和交換空間大小。
-
used 列:已經被使用的實體記憶體和交換空間大小。
-
free 列:還有多少實體記憶體和交換空間可用使用(真正尚未被使用的實體記憶體數量)。
在吞吐量固定的前提下,如果記憶體持續上漲,那麼很有可能是被測服務存在明顯的記憶體洩漏,需要使用 valgrind 等記憶體檢查工具進行定位。
-
shared 列:被共享使用的實體記憶體大小。
-
buffer/cache 列:被 buffer 和 cache 使用了的實體記憶體大小。
-
Linux 核心為了提升磁碟操作的效能,會消耗一部分空閒記憶體去快取磁碟資料,就是 buffer 和 cache。
-
如果給所有應用分配足夠記憶體後,實體記憶體還有剩餘,linux 會盡量再利用這些空閒記憶體,以提高整體 I/O 效率,其方法是把這部分剩餘記憶體再劃分為 cache 及 buffer 兩部分加以利用。
-
所以,
空閒實體記憶體不多,不一定表示系統執行狀態很差,因為記憶體的 cache 及 buffer 部分可以隨時被重用,在某種意義上,這兩部分記憶體也可以看作是額外的空閒記憶體。
-
-
available 列:還可以被應用程式使用的實體記憶體大小。
- 從應用程式的角度來說,
available = free + buffer + cache
。請注意,這只是一個很理想的計算方式,實際中的資料往往有較大的誤差。
- 從應用程式的角度來說,
釋放快取記憶體
方式一:手動釋放快取記憶體
snyc
echo 3 > /proc/sys/vm/drop_caches
free -m
方式二:修改 linux 配置自動釋放
/proc/sys/vm/drop_caches 這個值的 0 改為 1
磁碟 I/O
效能測試過程中,如果被測服務對磁碟讀寫過於頻繁,會導致大量請求處於 I/O 等待的狀態,系統負載升高,響應時間變長,吞吐量下降。
iostat 引數詳解
Linux 下可以用 iostat 命令來監控磁碟狀態。
iostat -d 2 10 表示每 2 秒統計一次基礎資料,統計 10 次:
-
tps:該裝置每秒的傳輸次數。“一次傳輸”意思是“一次 I/O 請求”。多個邏輯請求可能會被合併為“一次 I/O 請求”。“一次傳輸”請求的大小是未知的。
-
kB_read/s:每秒從裝置(driveexpressed)讀取的資料量,單位為 Kilobytes。
-
kB_wrtn/s:每秒向裝置(driveexpressed)寫入的資料量,單位為 Kilobytes。
-
kB_read:讀取的總資料量,單位為 Kilobytes。
-
kB_wrtn:寫入的總數量資料量,單位為 Kilobytes。
從 iostat -d 的輸出中,能夠獲得系統執行最基本的統計資料。但對於效能測試來說,這些資料不能提供更多的資訊。需要加上 -x 引數。
iostat -x 引數詳解
如 iostat -x 2 10 表示每 2 秒統計一次更詳細資料,統計 10 次:
-
rrqm/s:每秒這個裝置相關的讀取請求有多少被 Merge 了。
- 當系統呼叫需要讀取資料的時候,VFS 將請求發到各個 FS,如果 FS 發現不同的讀取請求讀取的是相同 Block 的資料,FS 會將這個請求合併 Merge。
-
wrqm/s:每秒這個裝置相關的寫入請求有多少被 Merge 了。
-
await:每一個 I/O 請求的處理的平均時間(單位:毫秒)。
-
await 的大小一般取決於服務時間(svtcm)以及 I/O 佇列的長度和 I/O 請求的發出模式。假設 svtcm 比較接近 await,說明 I/O 差點沒有等待時間。
-
假設 await 遠大於 svctm(如大於 5),就要考慮 I/O 有壓力瓶頸,說明 I/O 佇列太長,應用得到的響應時間變慢。
假設響應時間超過了使用者能夠容許的範圍,這時可以考慮更換更快的磁碟。
-
-
svctm:I/O 平均服務時間。
-
%util:在統計時間內有百分之多少用於 I/O 操作。
-
例如,如果統計間隔 1 秒,該裝置有 0.8 秒在處理 I/O,而 0.2 秒閒置,那麼該裝置的 %util = 0.8/1 = 80%,該引數暗示了裝置的繁忙程度。
-
%util 接近100% 表明 I/O 請求太多,I/O 系統繁忙,磁碟可能存在瓶頸。
-
iostat -x 完整引數如下:
- rrqm/s: 每秒進行 merge 的讀運算元目。即 delta(rerge)/s
- wrqm/s: 每秒進行 merge 的寫運算元目。即 delta(wmerge)/s
- t/s: 每秒完成的讀 I/O 裝置次數。即 delta(rioVs
- w/s: 每秒完成的寫 1/O 裝置次數。即 delta(wio)/s
- rsec/s: 每秒讀扇區數。即 delta(rsect)/s
- ws0c/s: 每秒寫扇區數。即 deita(wsect)/s
- rkB/s: 每秒讀 K 位元組數。是 rsect/s 的一半,因為每扇區大小為 512 位元組。(需要計算)
- wkB/s: 每秒寫 K 位元組數。是 wsect/s 的一半。(需要計算)
- avgrq+sz: 平均每次裝置 I/O 操作的資料大小(扇區)。delta(rsect+wsect)/delta(rio+wio)
- avgqu-sz: 平均I/O佇列長度,即delta(avea)/s/1000(因為 aveq 的單位為毫秒)。
- await: 平均每次裝置 I/O 操作的等待時間(毫秒)。即 delta(ruse+wuse)/delta(rio+wio)
- svctm: 平均每次裝置 I/O 操作的服務時間(毫秒)。即 delta(use)/delta(rio+wio)
- %util:一秒中有百分之多少的時間用於 I/O 操作,或者說一秒中有多少時間 I/O 佇列是非空的。即 delta(use)/s/1000(因為 use 的單位為毫秒)
網路
效能測試中網路監控主要包括網路流量、網路連線狀態的監控。
網路流量監控
方法很多,網上有很多 shell 指令碼。也可以使用 nethogs 命令。該命令與 top 類似,是一個實時互動的命令,執行介面如下:
在後臺服務效能測試中,對於返回文字結果的服務,並不需要太多關注在流量方面。
理解頻寬
針對一些特定的應用,比如直播或網盤(檔案上傳下載),頻寬瓶頸也是一個出現頻率較高的場景。
服務端的頻寬分為上行(out)和下行(in)頻寬(分別對應客戶端的下載和上傳)。
-
看視訊看新聞使用頻寬:客戶端的下載、服務端的上行頻寬。
-
服務端接收客戶端的資料使用頻寬:客戶端的上傳、服務端的下行頻寬。
一個 Web 伺服器如各類新聞網站通常需要更多的服務端上行(out)頻寬;而郵件伺服器、網盤伺服器等則通常需要更多的服務端下行頻寬(in)。
理解頻寬速率公式
-
1 Mb/s 頻寬速度為 128 KB/s(1024Kb / 8KB)
-
100 Mb/s 頻寬速度為 12.5 Mb/s(考慮網路損耗通常按 10M/s 或 1280KB/s 算)
示例:5000 萬畫素手機拍一張照片,照片大小約 20MB,在下述頻寬下需要耗時:
-
10M 頻寬約 20 秒:耗時 = 流量 / 速率 = 20MB / (10Mb/8) = 20 / 1.25 = 16 秒(按 1MB/s=128KB/s 速度算即 20 秒)
-
100M 頻寬約 2 秒:耗時 = 流量 / 速率 = 20MB / (100Mb/8) = 20 / 12.5 = 1.6 秒(按 10MB/s=128KB/s 速度算即 2 秒)
-
1000M 頻寬約 0.2 秒:耗時 = 流量 / 速率 = 20MB / (1000Mb/8) = 20 / 125 = 0.16 秒(按 100MB/s=128KB/s 速度算即 0.2 秒)
案例分析
現象:從監控圖表可以看出,當前的網路流量已經基本將網路頻寬佔滿,因此網路存在瓶頸。
解決方案:
- 硬體解決:增加頻寬(頻寬便宜)。
- 軟體解決:分析對應業務操作的資料傳送內容是否可精簡;是否可以非同步傳送。
網路連線狀態監控
效能測試中對網路的監控主要是監控網路連線狀態的變化和異常
。
-
對於使用 TCP 協議的服務,需要監控服務已建立連線的變化情況(即 ESTABLISHED 狀態的 TCP 連線)。
-
對於 HTTP 協議的服務,需要監控被測服務對應程式的
網路緩衝區的狀態、TIME_WAIT 狀態的連線數等
。
Linux 自帶的很多命令如 netstat、ss 都支援如上功能。
下圖是 netstat 對指定 pid 程式的監控結果:
完整命令輸出:
資料庫
慢查詢
更具體的慢 SQL 分析優化,可參見《MySQL 慢 SQL & 優化方案》。
如 MySQL 資源出現瓶頸,首先找慢查詢(超過自定義的執行時間閾值的 SQL)。
1)通過 SQL 語句定位到慢查詢日誌的所在目錄,然後檢視日誌。
show variables like "slow%";
2)慢查詢日誌在查詢結束以後才紀錄,所以在應用反映執行效率出現問題時,查詢慢查詢日誌並不能定位問題。這時可以使用show processlist
命令檢視當前 MySQL 正在進行的執行緒狀態,可以實時地檢視 SQL 的執行情況。
示例:
mysql -uroot -p123456 -h127.0.0.1 -p3307 -e "show full processlist" |grep dbname |grep -v NULL
3)找到慢查詢 SQL 後可以用執行計劃(explain)進行分析(或反饋給 DBA 和開發處理)。推薦最簡單的排查方式,步驟如下:
- 分析 SQL 是否載入了不必要的欄位/資料。
- 分析 SQL 是否命中索引。
- 如果 SQL 很複雜,優化 SQL 結構。
- 如果表資料量太大,考慮分表。
- ……
連線數
資料庫連線池的使用率
-
當資料庫連線池被佔滿時,如果有新的 SQL 語句要執行,只能排隊等待,等待連線池中的連線被釋放(等待之前的 SQL 語句執行完成)。
-
如果監控發現資料庫連線池的使用率過高,甚至是經常出現排隊的情況,則需要進行調優。
檢視/設定最大連線數
-- 檢視最大連線數
mysql> show variables like '%max_connection%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| extra_max_connections | |
| max_connections | 2512 |
+-----------------------+-------+
2 rows in set (0.00 sec)
-- 重新設定最大連線數
set global max_connections=1000;
在/etc/my.cnf 裡面設定資料庫的最大連線數
[mysqld]
max_connections = 1000
檢視當前連線數
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:表示當前連線數。跟 show processlist 結果相同。準確的來說,Threads_running 代表的是當前併發數。
-
Threads_running:表示啟用的連線數。一般遠低於 connected 數值。
-
Threads_created:表示建立過的執行緒數。
-
如果我們在 MySQL 伺服器配置檔案中設定了 thread_cache_size,那麼當客戶端斷開之後,伺服器處理此客戶的執行緒將會快取起來以響應下一個客戶而不是銷燬(前提是快取數未達上限)。
-
如果發現 Threads_created 值過大的話,表明 MySQL 伺服器一直在建立執行緒,這也是比較耗資源,因此可以適當增加配置檔案中 thread_cache_size 值。
-
查詢伺服器 thread_cache_size 的值
mysql> show variables like 'thread_cache_size';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| thread_cache_size | 100 |
+-------------------+-------+
1 row in set (0.00 sec)
鎖
詳見《MySQL 事務和鎖》。
快取命中率
-
通常,SQL 查詢是從磁碟中的資料庫檔案中讀取資料。
-
若當某一個 SQL 查詢語句之前執行過,則該 SQL 語句及查詢結果都會被快取下來,下次再查詢相同的 SQL 語句時,就會直接從資料庫快取中讀取。(注意,MySQL 8 開始已廢棄查詢快取功能。)
監控點
-
業務執行過程中 SQL 查詢時的快取命中率(查詢語句讀取快取的次數佔總查詢次數的比例)。
-
如果快取命中率過低,需要優化對應的程式碼和 SQL 查詢語句,以提高快取命中率。
案例分析
測試結果分析
結論:從目前的測試結果來看(如下圖所示),效能存在問題。
現象:併發數達到 50 時的 TPS 為 52,此時雖然響應時間為 4.4s(小於需求的 5s),但是資料庫伺服器的 CPU 使用率非常高(接近 100%),因此需要重點關注資料庫的調優分析。
排查過程
-
使用 top 命令觀察,確定是 mysqld 導致還是其他原因。
- CPU 分為使用者 CPU 和核心 CPU。綜合其他的各項資源指標來分析,發現記憶體、磁碟IO、網路等指標無任何異常,因此判斷此處不是核心 CPU 佔用高,主要原因是使用者程式佔用的 CPU 高。
- 確認目前 CPU 佔用高的為 mysqld 程式。
-
分析資料庫伺服器 CPU 高的可能原因:慢 SQL、SQL 語句過多、連線數過多等。
-
確認是否存在慢 SQL:
- 檢視慢查詢日誌,看看是否有超過預期指標的 SQL 語句,並分析排查:看看執行計劃是否準確、索引是否缺失、資料量是否太大等。
- 目前案例經過慢查詢日誌的分析,未存在慢查詢。
-
確認是否 SQL 語句過多或連線數過多:
- 使用
show full processlist
檢視當前資料庫中正在執行的 SQL 語句及連線池的狀態,發現大量 SQL 在等待執行。 - 再結合操作過程中的系統日誌進行分析,發現每進入一次商城首頁,就需要在資料庫中執行 19 條查詢 SQL。
- 使用
-
解決方案
- 硬體解決:增加 CPU。
- 軟體解決:為減少一次性載入過多 SQL,可考慮使用分批次、非同步載入的方式(展示到什麼位置,就查詢什麼位置的資料)。
JAVA 應用
JVM
JVM 簡介
JVM(JAVA Virtual Machine):虛擬出來的空間,專門供 JAVA 程式執行。
JVM 記憶體
重點關注:堆區(動態變化)
所有的物件在初始化都會申請堆區的空間,如果已申請的空間在使用結束後沒有及時地釋放,那麼該空間就會被佔用,即記憶體洩漏。
監控點:因此在測試時,需要關注堆區的空間是否持續上升而沒有下降。
案例分析
現象:堆記憶體使用是持續升高,無法降低到之前的水平。
解決方案:找到記憶體洩漏的程式碼,並優化程式碼。
垃圾回收機制
什麼是垃圾回收機制
-
垃圾回收指將記憶體中已申請並使用完成的那部分記憶體空間回收,供新申請使用。
-
垃圾回收機制都是針對堆區的記憶體進行的。
監控點
-
系統在做垃圾回收時,不能夠處理任何使用者業務的。如果垃圾回收過於頻繁,導致系統業務處理能力下降。
-
由於 Full GC 記憶體比較大,垃圾回收一次時間比較長,那麼這段時間內都不能處理業務,對系統影響比較大,因此我們需要關注
Full GC 頻率
。
垃圾回收機制的執行步驟如下:
-
新程式執行時需要先申請記憶體空間,會先從年輕代中申請。
-
在年輕代滿了以後,就會進行垃圾回收
Young GC
。 -
回收時檢查年輕代中的記憶體,是否還在使用。還在使用的部分會移存到生存區 2 中;不使用的部分則釋放,此時年輕代記憶體空間被清空。
-
新程式執行申請記憶體空間,再從年輕代申請。
-
年輕代又滿了,就會進行垃圾回收
Young GC
。還在使用的記憶體移存到生存區 1 中,並把生存區 2 中的記憶體也都存到生存區 1 中。此時就會清空年輕代和生存區 2。 -
迴圈上述 1-5 步。
-
如果部分記憶體在生存區中存活很久(記憶體在生存區中移動了 10 次左右),則將這部分記憶體放入到老年代中。
-
迴圈上述 1-7 步,直到老年代記憶體空間全部佔滿,此時就要進行垃圾回收
Full GC
。