測試分析及調(diào)優(yōu)
在進(jìn)行性能測試時,測試分析和調(diào)優(yōu)是確保系統(tǒng)達(dá)到預(yù)期性能目標(biāo)的重要步驟。本文介紹測試分析及性能調(diào)優(yōu)的相關(guān)流程和方法,幫助研發(fā)人員、測試人員或運(yùn)維人員快速地進(jìn)行性能測試、瓶頸定位及調(diào)優(yōu)。 系統(tǒng)的性能是由很多因素決定的,本文很難面面俱到,但是可以作為分析系統(tǒng)性能的一個指導(dǎo)。
適用對象和范圍
適用于需要進(jìn)行性能分析及調(diào)優(yōu)的工作。 預(yù)期讀者為測試管理人員、測試實(shí)施人員、技術(shù)支持人員、項(xiàng)目質(zhì)量管理人員、項(xiàng)目管理人員等系統(tǒng)技術(shù)質(zhì)量相關(guān)人員。
性能分析
前提
性能分析的前提除了需要豐富的性能測試監(jiān)控(如PTS自身的監(jiān)控、基礎(chǔ)類監(jiān)控-阿里云監(jiān)控、應(yīng)用類監(jiān)控-ARMS監(jiān)控等),還需要具備相關(guān)的技術(shù)知識(包括但不限于:操作系統(tǒng)、中間件、數(shù)據(jù)庫、開發(fā)等)。
流程
性能分析是一個系統(tǒng)化的過程,旨在識別和解決系統(tǒng)中的性能瓶頸。以下是具體的性能分析步驟:
很多情況下壓測流量并沒有完全進(jìn)入到后端(服務(wù)端),在網(wǎng)絡(luò)接入層(云化的架構(gòu),如SLB/WAF/高防IP,甚至是CDN/全站加速等)可能就會出現(xiàn)由于各種規(guī)格限制(如帶寬、最大連接數(shù)、新建連接數(shù)等),或者因?yàn)閴簻y的某些特征符合CC和DDoS的行為而觸發(fā)了防護(hù)策略導(dǎo)致壓測結(jié)果達(dá)不到預(yù)期。更多信息,請參見為什么后端壓力不大但壓測時報錯或超時?。
查看關(guān)鍵指標(biāo)是否滿足要求。如果不滿足,需要確定是哪個地方有問題,一般情況下,服務(wù)器端問題可能性比較大,也有可能是客戶端問題(這種情況非常少見)。
對于服務(wù)器端問題,需要分析硬件相關(guān)指標(biāo),例如CPU,Memory,Disk I/O和Network I/O。如果某個硬件指標(biāo)有問題,需要進(jìn)行深入分析。
如果硬件指標(biāo)都沒有問題,需要查看中間件相關(guān)指標(biāo),例如:線程池、連接池、GC等。
如果中間件相關(guān)指標(biāo)沒問題,需要查看數(shù)據(jù)庫相關(guān)指標(biāo),例如:SQL慢查、命中率、鎖、參數(shù)設(shè)置。
如果以上指標(biāo)都正常,應(yīng)用程序的算法、緩沖、緩存、同步或異步可能有問題,需要具體深入的分析。
瓶頸點(diǎn)
識別系統(tǒng)性能瓶頸是性能測試和調(diào)優(yōu)的關(guān)鍵步驟。性能瓶頸是指系統(tǒng)中限制整體性能的部分,通常是資源不足或效率低下的地方。以下是一些常見的系統(tǒng)性能瓶頸:
硬件、規(guī)格上的瓶頸
一般指的是CPU、內(nèi)存、磁盤I/O方面的問題。
中間件上的性能瓶頸
一般指的是應(yīng)用服務(wù)器、Web服務(wù)器等應(yīng)用軟件,還包括數(shù)據(jù)庫系統(tǒng)。 例如,中間件Weblogic平臺上配置的JDBC連接池的參數(shù)設(shè)置不合理造成的瓶頸。
應(yīng)用程序上的性能瓶頸
一般指的是開發(fā)人員開發(fā)出來的應(yīng)用程序。 例如,JVM參數(shù)設(shè)置不合理、容器配置不合理、慢SQL(可使用阿里云APM類產(chǎn)品如ARMS協(xié)助定位)、數(shù)據(jù)庫設(shè)計(jì)不合理、程序架構(gòu)規(guī)劃不合理或程序本身設(shè)計(jì)有問題(串行處理、請求的處理線程不夠、無緩沖、無緩存、生產(chǎn)者和消費(fèi)者不協(xié)調(diào)等),造成系統(tǒng)在大量用戶訪問時性能低下而造成的瓶頸。
操作系統(tǒng)上的性能瓶頸
一般指的是Windows、UNIX、Linux等操作系統(tǒng)。 例如,在進(jìn)行性能測試,出現(xiàn)物理內(nèi)存不足時,虛擬內(nèi)存設(shè)置也不合理,虛擬內(nèi)存的交換效率就會大大降低,從而導(dǎo)致行為的響應(yīng)時間大大增加,這時認(rèn)為操作系統(tǒng)上出現(xiàn)性能瓶頸。
網(wǎng)絡(luò)瓶頸
一般指的是防火墻、動態(tài)負(fù)載均衡器、交換機(jī)等設(shè)備。當(dāng)前更多的云化服務(wù)架構(gòu)使用的網(wǎng)絡(luò)接入產(chǎn)品:包括但不限于SLB、WAF、高防IP、CDN、邊緣安全加速等。 例如,在動態(tài)負(fù)載均衡器上設(shè)置了動態(tài)分發(fā)負(fù)載的機(jī)制,當(dāng)發(fā)現(xiàn)某個應(yīng)用服務(wù)器上的硬件資源已經(jīng)到達(dá)極限時,動態(tài)負(fù)載均衡器將后續(xù)的交易請求發(fā)送到其他負(fù)載較輕的應(yīng)用服務(wù)器上。在測試時發(fā)現(xiàn),動態(tài)負(fù)載均衡器沒有起到相應(yīng)的作用,這時可以認(rèn)為是網(wǎng)絡(luò)瓶頸。
方法
瓶頸點(diǎn)分析方法用于識別系統(tǒng)中的性能瓶頸,并找出影響系統(tǒng)性能的關(guān)鍵限制因素。以下是一些常見的瓶頸點(diǎn)分析方法:
CPU
如果CPU資源利用率很高的話,需要查看CPU消耗User、Sys、Wait哪種狀態(tài)。
如果CPU User非常高,需要查看消耗在哪個進(jìn)程,可使用
top
(Linux)命令查看,接著用top -H -p <pid>
查看哪個線程消耗資源高。如果是Java應(yīng)用,就可以用jstack
查看此線程正在執(zhí)行的堆棧,了解資源消耗在哪個方法上,通常查看源代碼就能知道問題所在。如果是C++應(yīng)用,可以用gprof
性能工具進(jìn)行分析。如果CPU Sys非常高,可以用
strace
(Linux)查看系統(tǒng)調(diào)用的資源消耗及時間。如果CPU Wait非常高,可能是磁盤讀寫問題,可以通過減少日志輸出、異步或升級到更快的存儲設(shè)備。
Memory
操作系統(tǒng)為了最大化利用內(nèi)存,一般都設(shè)置大量的Cache。因此,內(nèi)存利用率高達(dá)99%并不是問題,分析內(nèi)存問題主要查看某個進(jìn)程占用的內(nèi)存是否非常大以及是否有大量的Swap(虛擬內(nèi)存交換)。
磁盤I/O
磁盤I/O一個最顯著的指標(biāo)是繁忙率。可以通過減少日志輸出、異步處理或升級到更快的存儲設(shè)備來降低繁忙率。
網(wǎng)絡(luò)I/O
網(wǎng)絡(luò)I/O主要考慮傳輸內(nèi)容大小,不能超過硬件網(wǎng)絡(luò)傳輸?shù)淖畲笾?0%。可以通過壓縮減少內(nèi)容大小、在本地設(shè)置緩存以及分多次傳輸?shù)炔僮魈岣呔W(wǎng)絡(luò)I/O性能。
內(nèi)核參數(shù)
內(nèi)核參數(shù)一般都有默認(rèn)值,這些內(nèi)核參數(shù)默認(rèn)值對于一般系統(tǒng)沒問題,但是對于壓力測試來說,可能運(yùn)行的參數(shù)將會超過內(nèi)核參數(shù),導(dǎo)致系統(tǒng)出現(xiàn)問題,可以用
sysctl
來查看及修改。JVM
JVM主要分析GC、FULL GC是否頻繁,以及垃圾回收的時間。可以用
jstat
命令來查看,對于每個堆大小以及GC頻繁,通過jmap
將內(nèi)存轉(zhuǎn)儲,再借助工具HeapAnalyzer
來分析哪地方占用的內(nèi)存較高以及是否有內(nèi)存泄漏可能。簡單點(diǎn)可以使用APM工具,例如阿里云ARMS。線程池
如果線程不夠用,可以通過調(diào)整參數(shù)增加線程。對于線程池中的線程設(shè)置比較大但仍然不夠用的情況,可能是某個線程被阻塞來不及釋放、可能在等鎖、方法耗時較長、數(shù)據(jù)庫等待時間很長等原因?qū)е拢枰M(jìn)一步分析才能定位。
JDBC連接池
連接池不夠用的情況下,可以通過參數(shù)進(jìn)行調(diào)整增加。但是對于數(shù)據(jù)庫本身處理很慢的情況下,調(diào)整沒有多大的效果,需要查看數(shù)據(jù)庫方面以及因代碼導(dǎo)致連接未釋放的原因。
SQL
SQL效率低下也是導(dǎo)致性能差的一個非常重要的原因。可以通過查看執(zhí)行計(jì)劃看SQL慢在哪里,一般情況,SQL效率低下原因主要有:
類別
子類
表達(dá)式或描述
原因
索引
未建索引
無
產(chǎn)生全表掃描
未利用索引
substring(card_no,1,4)=′5378′
產(chǎn)生全表掃描
amount/30<1000
產(chǎn)生全表掃描
convert(char(10),date,112)=′19991201′
產(chǎn)生全表掃描
where salary<>3000
產(chǎn)生全表掃描
name like '%Tom'
產(chǎn)生全表掃描
first_name + last_name ='beill cliton'
產(chǎn)生全表掃描
id_no in(′0′,′1′)
產(chǎn)生全表掃描
select id from t where num=@num
有參數(shù)也會產(chǎn)生全表掃描
使用效能低的索引
oder by
非聚簇索引索引性能低
username='Tom' and age>20
字符串索引低于整型索引
表中列與空NULL值
索引性能低
盡量不要使用
IS NULL
或IS NOT NULL
索引性能低
數(shù)據(jù)量
所有數(shù)據(jù)量
select *
很多列產(chǎn)生大量數(shù)據(jù)
select id,name
表中有幾百萬行,產(chǎn)生大量數(shù)據(jù)
嵌套查詢
先不過濾數(shù)據(jù),后過濾數(shù)據(jù)
產(chǎn)生大量無用的數(shù)據(jù)
關(guān)聯(lián)查詢
多表進(jìn)行關(guān)聯(lián)查詢,先過濾掉小部分?jǐn)?shù)據(jù),在過濾大部分?jǐn)?shù)據(jù)
大量關(guān)聯(lián)操作
大數(shù)據(jù)量插入
一次插入
產(chǎn)生大量日志,消耗資源
鎖
鎖等待
update account set banlance=100 where id=10
產(chǎn)生行級鎖,將會鎖住整個表
死鎖
A:update a;update b;B:update b;update a;
將會產(chǎn)生死鎖
游標(biāo)
Cursor Open cursor,fetch;close cursor
性能很低
臨時表
create tmp table
創(chuàng)建臨時表產(chǎn)生大量日志
DROP TABLE
刪除臨時表
需要顯示刪除,避免系統(tǒng)表長時間鎖定
其他
EXISTS
代替IN
select num from a where num in(select num from b)
IN
會逐個判斷,EXISTS
有一條就結(jié)束EXISTS
代替select count(*)
判斷記錄是否存在
count(*)
將累加計(jì)算,EXISTS
有就結(jié)束BETWEEN
代替IN
ID in(1,2,3)
IN
逐個判斷,BETWEEN
是范圍判斷LEFT JOIN
代替NOT IN
select ID from a where ID not in(select b.Mainid from b)
NOT IN
逐個判斷,效率非常低UNION ALL
代替UNION
select ID from a union
select id from b union
刪除重復(fù)的行,可能會在磁盤進(jìn)行排序而
UNION ALL
只是簡單的將結(jié)果并在一起常用
SQL
盡量用綁定變量方法insert into A(ID) values(1)
直接寫SQL每次都要編譯,用綁定變量的方法只編譯一次,下次就可以用了
性能調(diào)優(yōu)
性能調(diào)優(yōu)是一個持續(xù)的過程,隨著應(yīng)用的變化和用戶負(fù)載的增加,需要不斷進(jìn)行監(jiān)控和調(diào)整。定期的性能測試和分析可以幫助及時發(fā)現(xiàn)問題,確保系統(tǒng)在各種負(fù)載條件下都能穩(wěn)定運(yùn)行。性能調(diào)優(yōu)涵蓋多個方面,包括代碼優(yōu)化、數(shù)據(jù)庫優(yōu)化、服務(wù)器配置、網(wǎng)絡(luò)優(yōu)化等。
調(diào)優(yōu)步驟
確定問題
應(yīng)用程序代碼:在通常情況下,很多程序的性能問題都是代碼問題。因此對于發(fā)現(xiàn)瓶頸的模塊,應(yīng)該首先檢查一下代碼。
數(shù)據(jù)庫配置:不合理的數(shù)據(jù)庫配置經(jīng)常引起整個系統(tǒng)運(yùn)行緩慢。一些大型數(shù)據(jù)庫都需要DBA進(jìn)行正確的參數(shù)調(diào)整才能投產(chǎn)的。
操作系統(tǒng)配置:不合理的參數(shù)設(shè)置可能引起系統(tǒng)瓶頸。
硬件設(shè)置:磁盤I/O、內(nèi)存大小等都是容易引起瓶頸的原因。
網(wǎng)絡(luò):網(wǎng)絡(luò)負(fù)載過重可能導(dǎo)致網(wǎng)絡(luò)沖突和網(wǎng)絡(luò)延遲。
分析問題
當(dāng)確定了問題之后,我們要明確這個問題影響的是響應(yīng)時間、吞吐量,還是其他問題?
是多數(shù)用戶還是少數(shù)用戶遇到了問題?如果是少數(shù)用戶,這幾個用戶與其他用戶的操作有什么不同?
系統(tǒng)資源監(jiān)控的結(jié)果是否正常?CPU的使用是否到達(dá)極限?I/O情況如何?
問題是否集中在某一類模塊中?
是客戶端還是服務(wù)器出現(xiàn)問題? 系統(tǒng)硬件配置是否夠用?
實(shí)際負(fù)載是否超過了系統(tǒng)的負(fù)載能力? 是否未對系統(tǒng)進(jìn)行優(yōu)化?
通過這些分析及一些與系統(tǒng)相關(guān)的問題,可以對系統(tǒng)瓶頸有更深入的了解,進(jìn)而分析出真正的原因。
確定調(diào)整目標(biāo)和解決方案
高系統(tǒng)吞吐量,縮短響應(yīng)時間,更好地支持并發(fā)。
測試解決方案
對通過解決方案調(diào)優(yōu)后的系統(tǒng)進(jìn)行基準(zhǔn)測試(基準(zhǔn)測試是指通過設(shè)計(jì)科學(xué)的測試方法、測試工具和測試系統(tǒng),實(shí)現(xiàn)對一類測試對象的某項(xiàng)性能指標(biāo)進(jìn)行定量的和可對比的測試)。
分析調(diào)優(yōu)結(jié)果
系統(tǒng)調(diào)優(yōu)是否達(dá)到或者超出了預(yù)定目標(biāo)。系統(tǒng)的整體性能是否得到了改善,還是以系統(tǒng)某部分性能來解決其他問題。調(diào)優(yōu)是否可以結(jié)束了。 最后,如果達(dá)到了預(yù)期目標(biāo),調(diào)優(yōu)工作可以先告一段落。
調(diào)優(yōu)注意事項(xiàng)
在應(yīng)用系統(tǒng)的設(shè)計(jì)開發(fā)過程中,應(yīng)始終把性能放在考慮的范圍內(nèi),將性能測試常態(tài)化,日常化內(nèi)網(wǎng)的性能測試,并定期對真實(shí)環(huán)境的業(yè)務(wù)進(jìn)行性能測試。這些PTS都可以支持。
確定清晰明確的性能目標(biāo)是關(guān)鍵,進(jìn)而將目標(biāo)轉(zhuǎn)化為PTS中的壓測場景并設(shè)置好需要的目標(biāo)量級,然后視情況選擇并發(fā)、TPS模式,以及自動遞增/手工調(diào)速的組合進(jìn)行流量控制。
必須保證調(diào)優(yōu)后的程序運(yùn)行正確。
系統(tǒng)的性能更大程度上取決于良好的設(shè)計(jì),調(diào)優(yōu)技巧只是一個輔助手段。
調(diào)優(yōu)過程是迭代漸進(jìn)的過程,每一次調(diào)優(yōu)的結(jié)果都要反饋到后續(xù)的代碼開發(fā)中去。
性能調(diào)優(yōu)不能以犧牲代碼的可讀性和可維護(hù)性為代價。
其他測試分析
成功率
成功率是根據(jù)服務(wù)端的返回值以及斷言來判斷的。如果沒有配置斷言的情況下,后端服務(wù)返回錯誤響應(yīng)碼、服務(wù)端異常或超時都認(rèn)為是失敗。
日志
日志是關(guān)于每個請求的內(nèi)容。采樣率10%可以理解為100個請求采集10個請求的內(nèi)容,采樣率100%的話表示每個請求都會記錄,但是會對施壓機(jī)性能造成影響,也會增加計(jì)費(fèi),日志采樣率不影響服務(wù)端的。
建立連接
建立連接就是建立HTTP連接的過程。超過設(shè)置的建立連接超時時間就認(rèn)為這個請求超時了,請求超時時間為從DNS查詢算起,到接收完響應(yīng)內(nèi)容整個時間的閾值。