
一、性能測(cè)試
Ⅰ.測(cè)試方法
- 微基準(zhǔn)性能測(cè)試
- 可以精準(zhǔn)定位到某個(gè)模塊或者某個(gè)方法的性能問(wèn)題,例如對(duì)比一個(gè)方法使用同步實(shí)現(xiàn)和非同步實(shí)現(xiàn)的性能差異
- 宏基準(zhǔn)性能測(cè)試
- 宏基準(zhǔn)性能測(cè)試是一個(gè)綜合測(cè)試,需要考慮到測(cè)試環(huán)境、測(cè)試場(chǎng)景和測(cè)試目標(biāo)
- 測(cè)試環(huán)境:模擬線上的真實(shí)環(huán)境
- 測(cè)試場(chǎng)景:在測(cè)試某個(gè)接口時(shí),是否有其他業(yè)務(wù)的接口也在平行運(yùn)行,進(jìn)而造成干擾
- 測(cè)試目標(biāo)
- 可以通過(guò)吞吐量和響應(yīng)時(shí)間來(lái)衡量系統(tǒng)是否達(dá)標(biāo),如果不達(dá)標(biāo),就需要進(jìn)行優(yōu)化
- 如果達(dá)標(biāo),就繼續(xù)加大測(cè)試的并發(fā)數(shù),探底接口的TPS
- 除了關(guān)注接口的吞吐量和響應(yīng)時(shí)間外,還需要關(guān)注CPU、內(nèi)存和IO的使用率情況
Ⅱ.干擾因素
1.熱身問(wèn)題
①. 在Java編程語(yǔ)言和環(huán)境中,.java文件編譯成.class文件后,需要通過(guò)解析器將字節(jié)碼轉(zhuǎn)換成本地機(jī)器碼才能運(yùn)行
②. 為了節(jié)約內(nèi)存和執(zhí)行效率,代碼在最初被執(zhí)行時(shí),解析器會(huì)率先解析執(zhí)行這段代碼
③. 隨著代碼被執(zhí)行的次數(shù)增加,當(dāng)JVM發(fā)現(xiàn)某個(gè)方法或代碼塊運(yùn)行得很頻繁時(shí),就會(huì)把這些代碼認(rèn)定為熱點(diǎn)代碼
- 為了提高熱點(diǎn)代碼的執(zhí)行效率,在運(yùn)行時(shí),JVM將通過(guò)即時(shí)編譯器(JIT)把這些代碼編譯成與本地平臺(tái)相關(guān)的機(jī)器碼
- 并進(jìn)行各層次的優(yōu)化,然后存儲(chǔ)在內(nèi)存中,之后每次運(yùn)行代碼時(shí),直接從內(nèi)存中獲取
④. 因此在剛開始運(yùn)行的階段,JVM會(huì)花費(fèi)很長(zhǎng)的時(shí)間來(lái)全面優(yōu)化代碼,后面就能以最高性能運(yùn)行了
2. 測(cè)試結(jié)果不穩(wěn)定
①. 不穩(wěn)定因素:機(jī)器其他進(jìn)程的影響、網(wǎng)絡(luò)波動(dòng)、JVM GC的不確定性
①. 解決方案:通過(guò)多次測(cè)試,將測(cè)試結(jié)果求平均,只要能保證平均值在一個(gè)合理的范圍之內(nèi),并且波動(dòng)不大即可
3. 多JVM
①. 任意一個(gè)JVM都擁有整個(gè)系統(tǒng)的資源使用權(quán)
②. 如果一臺(tái)機(jī)器上只部署單獨(dú)的一個(gè)JVM,在做性能測(cè)試時(shí),測(cè)試結(jié)果會(huì)很好,但一臺(tái)機(jī)器上有多個(gè)JVM,則不一定
③. 盡量避免線程環(huán)境一臺(tái)機(jī)器部署多個(gè)JVM
二、性能分析
1.完成性能測(cè)試之后,需要輸出一份性能測(cè)試報(bào)告,測(cè)試結(jié)果需要包括
- 測(cè)試接口的吞吐量和響應(yīng)時(shí)間(平均、最大、最小)
- 服務(wù)器的CPU、內(nèi)存、磁盤IO、網(wǎng)絡(luò)IO使用率、JVM的GC情況
2.通過(guò)觀察性能指標(biāo),可以發(fā)現(xiàn)性能瓶頸,再通過(guò)自下而上的方式分析查找問(wèn)題
- 首先從操作系統(tǒng)層面,查看系統(tǒng)的CPU、內(nèi)存、磁盤IO、網(wǎng)絡(luò)IO的使用率是否存在異常
- 再通過(guò)命令查找異常日志,通過(guò)分析日志,尋找導(dǎo)致性能瓶頸的原因
- 還可以從Java應(yīng)用的JVM層面下手,查看JVM的GC頻率以及內(nèi)存分配情況是否存在異常
- 如果系統(tǒng)和JVM層面都沒(méi)有出現(xiàn)異常情況,可以查看應(yīng)用服務(wù)業(yè)務(wù)層是否存在性能瓶頸
- 例如Java編程的問(wèn)題、讀寫數(shù)據(jù)瓶頸
3.分析查找性能問(wèn)題可以采用自下而上的方式,而解決性能問(wèn)題,一般采用自上而下的方式逐級(jí)優(yōu)化
三、性能調(diào)優(yōu)
思路:業(yè)務(wù)調(diào)優(yōu) -> 編程調(diào)優(yōu) -> 系統(tǒng)調(diào)優(yōu)
Ⅰ. 優(yōu)化代碼
1.應(yīng)用層的問(wèn)題代碼往往會(huì)因?yàn)楹谋M系統(tǒng)資源而暴露出來(lái)
2.例如某段代碼導(dǎo)致內(nèi)存溢出,這往往是將JVM的內(nèi)存耗盡了
- 這會(huì)引發(fā)JVM頻繁地發(fā)生GC,導(dǎo)致CPU居高不下,此時(shí)也會(huì)耗盡系統(tǒng)的CPU資源
3.還有一些非問(wèn)題代碼導(dǎo)致的性能問(wèn)題,比較難以發(fā)現(xiàn)
- 例如如果對(duì)LinkedList進(jìn)行for循環(huán)遍歷,每次循環(huán)獲取元素時(shí),都會(huì)遍歷一次list,讀效率很低
- 優(yōu)化方案:可以采用Iterator
Ⅱ. 優(yōu)化設(shè)計(jì)
1.面向?qū)ο笥泻芏嘣O(shè)計(jì)模式,可以用于優(yōu)化業(yè)務(wù)層以及中間件層的代碼設(shè)計(jì),進(jìn)而達(dá)到精簡(jiǎn)代碼和提高整體性能的目的
2.例如單例模式在頻繁創(chuàng)建對(duì)象的場(chǎng)景中,可以共享一個(gè)對(duì)象,減少頻繁創(chuàng)建和銷毀對(duì)象帶來(lái)的性能開銷
Ⅲ. 優(yōu)化算法
1.合適的算法可以大大提升系統(tǒng)性能
2.例如在不同的場(chǎng)景中,使用合適的查找算法可以降低時(shí)間復(fù)雜度
Ⅳ. 時(shí)間換空間
1.如果系統(tǒng)對(duì)查詢的速度沒(méi)有很高的要求,但對(duì)存儲(chǔ)空間要求苛刻,可以考慮用時(shí)間換空間
2.例如String的intern方法,可以將重復(fù)率比較高的數(shù)據(jù)存儲(chǔ)在常量池,重復(fù)使用相同的對(duì)象,大大節(jié)省內(nèi)存空間
- 但由于常量池使用的是HashMap類型,如果存儲(chǔ)數(shù)據(jù)過(guò)多,就會(huì)導(dǎo)致查詢性能下降
Ⅴ. 空間換時(shí)間
1.使用存儲(chǔ)空間來(lái)提升訪問(wèn)速度
2.例如MySQL的分庫(kù)分表
Ⅵ. 參數(shù)調(diào)優(yōu)
1.根據(jù)業(yè)務(wù)場(chǎng)景,合理地設(shè)置JVM的內(nèi)存空間和GC算法
2.另外,合理地設(shè)置Web容器的線程池大小和Linux操作系統(tǒng)的內(nèi)核參數(shù)
四、兜底策略
1.性能優(yōu)化策略,主要為了提高系統(tǒng)性能,而兜底策略,主要為了確保系統(tǒng)的穩(wěn)定性
2.限流
- 對(duì)系統(tǒng)的入口設(shè)置最大訪問(wèn)限制,參考性能測(cè)試中探底的接口TPS
- 同時(shí)采用熔斷措施,友好地返回沒(méi)有成功的請(qǐng)求
3.智能橫向擴(kuò)容
- 當(dāng)訪問(wèn)量超過(guò)某一個(gè)閾值時(shí),系統(tǒng)可以根據(jù)需求自動(dòng)橫向擴(kuò)容
4.提前擴(kuò)容
- 常用于高并發(fā)系統(tǒng),例如瞬時(shí)搶購(gòu)
- 此時(shí)智能橫向擴(kuò)容無(wú)法滿足大量發(fā)生在瞬間的請(qǐng)求
5.Kubernetes可以實(shí)現(xiàn)智能橫向擴(kuò)容和提前擴(kuò)容Docker服務(wù)
五、總結(jié)

寫在最后
