常見的互聯網架構中,通常都能看到spring+mybatis+mysql+redis搭配的身影,在我所服務的公司亦是如此。通常來講,應用內部的接口都是直接調用的,所謂的面向接口編程,應用間的調用直接調或者經過相似dubbo之類的服務框架來執行,數據格式每每採用json,即統一也方便各數據間作轉換和取值,緩存通常使用redis或memcached,存儲一些對象或json格式的字符串。對外提供的接口,通常都須要進行壓力測試,以便估算其性能,併爲後續的調優提供指導方向,如下接口即是在壓測過程當中出現的各類「奇怪現象」,所謂奇怪,指的是從表象上看與咱們正常的邏輯思路不符,但其本質仍是咱們對壓力下程序的表現出來的特徵不熟悉,用慣用的知識結構試圖去解釋,這根本是行不通的。下文是我在一次全面壓測過程後對數據進行的分析彙總,其中的現象是不少壓測常見的,裏面的分析過程及改進措施我認爲有很大的參考意義。具體內容以下:(部分接口爲了安全我省略了其名稱,但不影響咱們的分析,另外形如1N3T之類的表示的是1臺nginx,3臺tomcat,具體的tps數值只是爲了說明優化先後的比照,沒有實際意義)前端
舊框架,平均響應時間長,應用CPU高,程序內部有大量的bean到map到json之間的轉換,修改數據庫鏈接數後,tps沒有提高。java
重構系統,用mybatis替代以前的dao操做,減小bean-map-json之間的內部數據轉換,減小程序內部的無用操做。mysql
tps改進後能到3000左右,有較大提高,但壓測時應用cpu幾乎跑滿,還有改善空間。nginx
單臺應用,數據庫資源cpu都能到50%,10臺tomcat在1萬併發下數據庫cpu跑滿,load值700多,但db的qps也不過11554,並不算多,所以懷疑是sql執行耗費了cpu,多是某條sql沒有按索引查找或者索引失效。redis
查看SQL文件發現以下sql:select count(1) from orders where order_status_id !=40 ,將其改成select order_id from orders 而後經過程序把order_status_id!=40的過濾掉。經過list.size()來獲取。order_status_id即便加了索引,因爲是!=比較,因此不會去按索引查找,致使cpu高spring
相同環境下(1臺nginx,10臺tomcat,1000併發),tps由3000變成3727,略有增加,可是db的cpu明顯降低,變爲30%,效果明顯sql
後端都是15臺tomcat,前端1臺nginx時tps爲4552,經過lvs掛10臺nginx時爲9608,增加不明顯,其nginx和tomcat都壓力不大,集羣結果不合理,懷疑是nginx轉發配置的問題;數據庫
未進一步改進:多是須要調整nginx的參數,以前發現過nginx不一樣的配置對後端集羣環境的tps影響很大編程
舊框架,程序內部有不少Bo-map-json相互的轉換json
刪除冗餘代碼、更換鏈接池包,使用mybatis
Tps由2000多增加爲8000多,db的qps爲9000左右,優化後壓測應用的cpu佔用高,幾乎跑滿。
1N1T和1N10T的tps同樣,都爲5000,增大併發時錯誤數增多,應用cpu耗費70%,db無壓力,nginx單臺經過ss –s 發現端口占滿,即nginx到tomcat之間轉發的鏈接端口time-wait狀態6萬多。Nginx存在瓶頸。
調優nginx參數,將短鏈接改成長鏈接
1N3T的tps能到17376,tomat的cpu壓力84%,db的qps18000,cpu69%,應用的資源基本使用到量。
增長應用服務器,tps增加不明顯,且nginx、tomcat、db的負載都不高,說明服務器自己不是瓶頸,考慮是否是網絡的問題,經過監控網卡包流量發現網絡數據跑滿,由於此接口會有大量數據的輸出,所以瓶頸在網絡上。另外,測試過程當中發現redis有報錯,redis服務器是虛機,可能服務能力有限。
開啓tomcat的gzip壓縮。
同等併發下(1臺nginx,10臺tomcat,1000併發),tps由3727增加到10022,增加了近3倍,效果顯著。
通過tomcat的啓用gzip後,1N10T下tps爲10022,需進一步提高。
優化nginx:
l nginx日誌關閉
l nginx進程數量worker,由24改成16
l nginx keepalive數量由256改成2048
Tps由10022提高爲13270。
1N10T的tps爲1萬3千多,1N5T的tps爲1萬2千多,相差不大,應用的tomcat資源利用沒滿,cpu爲65%,Db的QPS已經到2萬多了,單臺服務器db基本上到量了,所以再增長應用也沒效果,只會致使響應的時間變長。
單臺db已經沒法改進了,要不提高服務器硬件,要不讀寫分離。
此接口經過redis取數據,tps不高才1000多,但cpu佔用了80%,說明程序內部有大量序列化反序列化的操做,多是json序列化的問題。
將net.sf.json改爲alibaba的fastjson。
同等併發條件下tps由1000多提高爲5000多,提升了近5倍。
此接口根據參數從redis中獲取數據,每一個參數與redis交互一次,當一組參數時tps5133,五組參數時tps1169,屢次交互影響了處理性能。
將從redis獲取數據的get改成mget,減小交互次數。
五組參數時1N3T壓測TPS9707,據此估算即便是單臺tomcat,tps也能有三四千,性能比單次get的調用方式提高了3,4倍。
此處說的是可能,由於nginx服務器的cpu雖然不高,但pps已經800多k,此時應該是nginx的服務器網絡流量成爲了瓶頸。(只是猜想)
能夠增長多臺nginx負載,前端加lvs
1N3T在2000併發下tps爲9849,此時db的qps爲90000,CPU80%,將tomcat增到10臺,5000併發下,tps爲7813,db的qps爲19000,cpu75%,load 1,說明壓力增大狀況下db的壓力反而下來了,注意到nginx服務器的網卡流量達到885M,說明是壓力過大狀況下,網絡滿了,發生丟包,致使db端壓力反而下來了。
注意壓測狀況下部分接口因爲數據量傳輸較大,會出現網絡瓶頸。
雖然缺乏應用的cpu及db的cpu利用率數據,較低的tps應該是應用的瓶頸,且須要關注是否是db在處理查詢的時候緩慢。
1.鏈接池由dbcp改成hikar;
2.減小了日誌打印輸出
3.sql優化,將部分條件過濾改成在java代碼中執行
Tps由不到500增加爲1300多。
查看是否存在瓶頸資源,能夠看到5臺tomcat集羣下,tps爲9952,但db的qps爲5-6萬,cpu利用率爲37%,說明對數據庫進行了大量的主鍵或索引查詢,通常單臺db的qps也就4萬左右,再增長應用的集羣數量,對tps也不會有太大影響。
能夠考慮分庫
18nginx,2tomcat時tps8100,此時應用服務器的端口數滿, 通常來講,Nginx短鏈接在高併發下容易致使端口占滿的問題。
將nginx改成長鏈接
tps增加爲10733,TPS穩定,起伏減小,可是CPU耗盡。說明cpu打滿了,此時若是還要優化就的進行代碼調優了。
18臺nginx,20臺tomcat,tps才6842,此時應用cpu利用率85%,說明cpu存在瓶頸,但檢查此接口並未作大計算量的工做,有多是日誌的級別問題,cpu在頻繁的打日誌。
將日誌級別由debug級改成info級
同等環境tps由6842增加爲23592.坑爹的生產環境debug日誌級別。