性能瓶頸分析思路
性能分析是一個大課題,不同的架構、不同的應用場景、不同的程序語言分析的方法各有差異,抽象一下大致分為二類:
自底向上:通過監控硬件及操作系統性能指標(CPU、內存、磁盤、網絡等硬件資源的性能指標)來分析性能問題(配置、程序等的問題)。因為用戶請求最終是由計算機硬件設備來完成的,做事的是 CPU。
自頂向下:通過生成負載來觀察被測試的系統性能,比如響應時間、吞吐量;然后從請求起點由外及里一層一層的分析,從而找到性能問題所在。
不管是自底向上還是自頂向下,關鍵點就是生成負載、監控性能指標。上面我們說了兩種方法,大家會問哪一種方法更好呢?哪種方法更簡單一點呢?我想說的是方法無所謂好壞,只是一個思路。
對於沒有經驗的性能測試工作者,提倡自底向上;對於經驗豐富的性能測試工作者,先用自頂向下的方式解決掉明顯性能問題,再接合自底向上的方式分析更深層次的問題。
性能分析流程
從性能測試工程師的角度來講解一下通常的性能分析過程(操作系統以Linux為例)。
| 序號 |
步驟名稱 |
說明 |
| 1.0 |
檢查RT |
模擬用戶發起負載后,采用自頂向下的方式首先分析RT(響應時間)。 1.如果RT小,TPS大,說明性能良好。 2.如果RT小,TPS小,檢查負載機資源占用情況,確定是否要加大負載。 3.如果RT大,TPS小,檢查負載機的資源消耗,確保不是負載機的性能原因導致的RT變大。 |
| 1.1 |
檢查TPS |
1.TPS大時RT小,說明性能良好 2.TPS小時RT大,檢查負載機的資源消耗,確保不是負載機的性能原因導致的RT變大。 |
| 2.0 |
檢查負載機資源消耗耗 |
1.檢查CPU使用率,CPU負載(LoadAverage)確認是用戶CPU占用高還是系統CPU占用高?確認測試腳本沒有性能問題,不會造成結 果統計的不准確 2.檢查內存使用情況,確認並發內存泄露風險,不會造成結果統計的不准確。 3.檢查網絡占用帶寬。大家可以做個小實驗,一台並發100用戶,一台運行一個用戶,比較兩者的響應時間是否在同一個數量級,從而評估負載機對性能的影響。 |
| 2.1 |
判下負載機是否有性能問題 |
排除負載機的性能問題,確保測試結果可參考。 |
| 3.0 |
檢查Web 服務器的資源消耗 |
1.檢查CPU使用率,確認用戶CPU與系統CPU占用情況 1.1系統CPU占用多,檢查系統調用情況,找出是哪個進程或線程,確認調用是否正常?比如濫寫日志,導致系統CPU利用率高。 1.2用戶CPU高,找出進程或線程,定位到程序,確認是否調用正常?一般來說Web服務程序不涉及到運算的,CPU利用率高要確認是否是其它資源等待導致的CPU利用率高。 1.2.1比如IO等待會帶來CPU利用率高,CPU由於等待IO,而頻繁的進行上下文切換。 1.2.2比如事務過程較長,需要鎖定的資源較多,而請求也較多時,CPU需要不停的中斷、上下文切換去處理獲取到資源的請求。 2.檢查內存使用情況,找出占內存多的進程或線程 3.檢查磁盤使用情況,找出IO量大的進程或線程 4.檢查占用的帶寬 5.分析Web頁面響應的時間組成 |
| 3.1 |
確認是否 Web服務器瓶頸 |
從3.0監控指標判斷是否是Web服務器硬件性能瓶頸。 |
| 3.2 |
檢查中間 件配置 |
1.檢查中間件線程池活動連接數,確認是否是此配置問題。 2.檢查Heap配置,確認gc的影響。 |
| 3.3 |
是否是中間件限制 |
1.監控線程池活動連接數,確認線程池夠用。 2.監控線程狀態,如果長期是Blocked狀態,可能是響應慢,有可能會導致死鎖,Dump線程棧找到疑問程序。 3.監控JVM,關注GC,評估Heap空間是否夠用。 |
| 4.0 |
檢查APP 服務器資源消耗 |
分析方法同3.0。 |
| 4.1 |
確認是否 App服務器瓶頸 |
從3.0監控指標判斷是否是App服務器硬件性能瓶頸 |
| 4.2 |
檢查中間 件配置 |
1.檢查中間件線程池。 2.檢查數據庫連接池。 3.檢查Heap配置。 |
| 4.3 |
是否是中間件限制 |
1.監控線程池,確認線程池夠用。 2.監控線程狀態,如果長期是Blocked狀態,代表響應慢,有可能會導致死鎖。 3.監控與數據庫的連接數,確認數據庫連接池是否夠用。 4.監控JVM,關注GC,評估Heap空間是否夠用。可以借助JVisualVM、 Jprofiler來分析,也可以使用Jdk自帶的監控命令。 |
| 5.0 |
數據庫服務器資源消耗分析 |
1.CPU消耗,CPU負載。 2.內存消耗。 3.IO繁忙程度。 4.數據庫監控 4.1慢查詢。 4.2對DB不熟悉的讀者可以找DBA幫忙監控分析。 |
| 5.1 |
是否是DB 性能問題 |
由5.0的監控結果來判斷是否是DB性能問題。 |
linux主機瓶頸閥值分析
CPU定位分析
| 模塊 |
類型 |
度量方法 |
衡量標准 |
| CPU |
使 用 情況 |
|
注意>=50% 告警>=70% 嚴重>=90% |
| CPU |
滿載 |
|
運行的隊列大於cpu邏輯顆數時,證明已經有一定的負載了,不過這個計數也不絕對,需進一步分析其他的資源情況來斷定是否 CPU已經滿負荷運作 |
我們可以用的命令有vmstat, top ,sar,dstat, mpstat, ps 等命令來進行統計分析。
vmstat 字段含義說明:

| 類別 |
項目 |
含義 |
說明 |
| Procs(進程) |
r |
等待執行的任務數 |
展示了正在執行和等待cpu資源的任務個數。當這個值超過了cpu個數,就會出現cpu瓶頸。 |
| B |
等待IO的進程數量 |
|
|
| Memory(內存) |
swpd |
正在使用虛擬的內存大小,單位k |
|
| free |
空閑內存大小 |
|
|
| buff |
已用的buff大小,對塊設備的讀寫進行緩沖 |
|
|
| cache |
已用的cache大小,文件系統的cache |
|
|
| Swap |
si |
每秒從交換區寫入內存的大小(單位:kb/s) |
|
| so |
每秒從內存寫到交換區的大小 |
|
|
| IO |
bi |
每秒讀取的塊數(讀磁盤) |
現在的Linux版本塊的大小為1024bytes |
| bo |
每秒寫入的塊數(寫磁盤) |
|
|
| system |
in |
每秒中斷數,包括時鍾中斷 |
這兩個值越大,會看到由內核消耗的cpu時間會越多 |
| cs |
每秒上下文切換數 |
||
| CPU(以百分比表示) |
Us |
用戶進程執行消耗cpu時間(user time) |
us的值比較高時,說明用戶進程消耗的cpu時間多,但是如果長期超過50%的使用,那么我們就該考慮優化程序算法或其他措施了 |
| Sy |
系統進程消耗cpu時間(system time) |
sys的值過高時,說明系統內核消耗的cpu資源多,這個不是良性的表現,我們應該檢查原因。 |
|
| Id |
空閑時間(包括IO等待時間) |
|
|
| wa |
等待IO時間 |
Wa過高時,說明io等待比較嚴重,這可能是由於磁盤大量隨機訪問造成的,也有可能是磁盤的帶寬出現瓶頸。 |
內存定位分析
| 模塊 |
類型 |
度量方法 |
衡量標准 |
| 內存 |
使用情況 |
|
注意>=50% 告警>=70% 嚴重>=80% |
| 內存 |
滿載 |
|
OOM機制 |
我們可以用的命令有 vmstat,sar,dstat, free, top , ps 等命令來進行統計分析。
free 字段含義說明:

|
參數
|
釋義
|
|---|---|
| total | 內存總數,物理內存總數 |
| used | 已經使用的內存數 |
| free | 空閑的內存數 |
| shared | 多個進程共享的內存總額 |
| buffers/cache | 緩存內存數 |
| available | 可用內存數 |
| Swap | 交換分區,虛擬內存 |
網絡定位分析
| 模塊 |
類型 |
度量方法 |
衡量標准 |
| 網絡 |
使用情況 |
|
|
| 網絡 |
滿載 |
|
統計的丟包有計數證明已經滿了 |
| 網絡 |
錯誤 |
|
錯誤有計數 |
衡量系統網絡的使用情況,我們可以使用的命令有ifstat,iftop,netstat以及查看net的速率,通過查看發現收發包的吞吐速率達到網卡的最大上限,網絡數據報文有因為這類原因而引發的丟包
阻塞等都證明當前網絡可能存在瓶頸。在進行性能測試時為了減小網絡的影響,一般我們都是在局域網中進行測試執行。
nicstat命令詳解

磁盤IO定位分析
| 模塊 |
類型 |
度量方法 |
衡量標准 |
| IO |
使用情況 |
|
注意>=40% 告警>=60% 嚴重>=80% |
| IO |
滿載 |
|
IO 已經有滿載嫌疑 |
| IO |
錯誤 |
|
有信息 |
衡量系統IO的使用情況,我們可以使用的命令有sar, iostat,iotop等命令進行系統級的IO監控分析。當發現IO的利用率大於40%時候,就需要注意了,當使用率大於60%則處於告警階段,大於80%IO就會出現阻塞了。
iostat -x命令詳解

|
選項
|
說明
|
|---|---|
| rrqm/s | 每秒對該設備的讀請求被合並次數,文件系統會對讀取同塊(block)的請求進行合並 |
| wrqm/s | 每秒對該設備的寫請求被合並次數 |
| r/s | 每秒完成的讀次數 |
| w/s | 每秒完成的寫次數 |
| rkB/s | 每秒讀數據量(kB為單位) |
| wkB/s | 每秒寫數據量(kB為單位) |
| avgrq-sz | 平均每次IO操作的數據量(扇區數為單位) |
| avgqu-sz | 平均等待處理的IO請求隊列長度 |
| await | 平均每次IO請求等待時間(包括等待時間和處理時間,毫秒為單位) |
| svctm | 平均每次IO請求的處理時間(毫秒為單位) |
| %util | 采用周期內用於IO操作的時間比率,即IO隊列非空的時間比率 |
tomcat 性能監控和分析
連接器(Connector)的參數配置
- HTTP/1.1:默認值,使用的協議與Tomcat版本有關
- org.apache.coyote.http11.Http11Protocol:BIO
- org.apache.coyote.http11.Http11NioProtocol:NIO
- org.apache.coyote.http11.Http11Nio2Protocol:NIO2
- org.apache.coyote.http11.Http11AprProtocol:APR
|
Attribute
|
描述
|
|---|---|
|
maxConnections |
服務器在任何給定時間接受和處理的最大連接數。當達到這個數字時,服務器將接受一個進一步的連接,但不會處理。這個附加連接將被阻塞,直到正在處理的連接數降到maxConnections以下,服務器再次開始接受並重新處理新的連接。 |
| acceptCount | 所有可能的請求處理線程正在使用時,傳入連接請求的最大隊列長度。 當隊列滿時收到的任何請求都將被拒絕。 默認值為100。 |
| connectionTimeout | 連接器在接受連接后等待的請求URI行的毫秒數。 使用值-1表示無(即無窮大)超時。 默認值為60000(即60秒),但請注意Tomcat附帶的標准server.xml將其設置為20000(即20秒)。 除非disableUploadTimeout設置為false,否則讀取請求主體(如果有的話)也將使用此超時。 |
| keepAliveTimeout | 連接器在關閉連接之前等待另一個HTTP請求的毫秒數。 默認值是使用為connectionTimeout屬性設置的值。 使用值-1表示無(即無窮大)超時。 |
| maxKeepAliveRequests | 在服務器關閉連接之前可以進行流水線處理的最大HTTP請求數。將此屬性設置為1將禁用HTTP / 1.0保持活動狀態,以及HTTP / 1.1保持活動狀態和流水線狀態。將其設置為-1將允許無限量的流水線或保持活動的HTTP請求。如果未指定,則此屬性設置為100。 |
| executor | 對Executor元素中的名稱的引用。 如果設置了此屬性,並且命名的執行程序存在,則連接器將使用執行程序,並且所有其他線程屬性將被忽略。 請注意,如果未為連接器指定共享執行程序,則連接器將使用專用的內部執行程序來提供線程池。 |
線程池Executor的參數配置
- name:該線程池的標記
- maxThreads:線程池中最大活躍線程數,默認值200(Tomcat7和8都是)
- minSpareThreads:線程池中保持的最小線程數,最小值是25
- maxIdleTime:線程空閑的最大時間,當空閑超過該值時關閉線程(除非線程數小於minSpareThreads),單位是ms,默認值60000(1分鍾)
- daemon:是否后台線程,默認值true
- threadPriority:線程優先級,默認值5
- namePrefix:線程名字的前綴,線程池中線程名字為:namePrefix+線程編號
tomcat監控
對tomcat的監控主要就是查看服務器中的連接數和線程數,以及線程狀態的監控
查看大致分為兩種方案:(1)使用現成的工具,(2)直接使用Linux的命令查看。
命令方式:
查看線程數
ps -Lf 163610 |wc -l
查看連接數
netstat -nat|awk '{print $6}'|sort|uniq -c|sort
工具監控:tomcat manager,JProfiler,JConsole,jvisualvm, Btrace等
下圖是tomcat manager監控頁面

線程問題
1、Deadlock問題必須要解決,
2、大量線程處於Blocked狀態 waiting狀態
3、線程不夠用
4、線程占用資源高需要分析

命令行分析資源消耗最高的線程

實例講解
下面已本次支付性能測試的一個性能問題為實例來講解整個分析過程
1、通過查看tps和響應時間,確認存在性能瓶頸


2,檢查日志,發現有錯誤日志,首先解決錯誤日志問題,但是性能問題依舊未解決

3、監控服務進程資源,發現cpu利用率為200%多,內存, IO都無瓶頸
4、監控可以看出時間都消耗在tss服務內部,考慮通過線程分析是否有阻塞導致,通過命令jstack打印線程堆棧信息
jstack -l PID >tmp.txt


