最近一次公司服務出了一些性能的問題,主要是內存不釋放。
領到任務后就開始展開工作。項目是用.net core 6寫的,在框上應該不會有什么問題,這是大背景。另外服務是部署在k8s上的,于是就和性能測試人員,開發人員搭測試環境,展開問題重現的測試。
問題很簡單,集中在排查幾個大查詢,并發10個大查詢,每次請求數據在數十萬到上百萬條記錄,pod的內存就很快飆上去了,并且之后不釋放。重現了,就看看怎么解決吧,內存不釋放,第一個想到的就是手動GC一下看看什么現象,結果是手動GC是管理用的,那就說明服務內部沒有有效的調用GC,或調用的GC的釋放內存的速度,沒有請求增長內存的速度快。
這個問題可以通過修改項目配置,開啟項目工作站后臺并發回收模式,就能有效地改善。這是個配置,很容易做到,于是修改完后推到鏡像倉庫,并部署在性能pod中,再次進行測試。同樣的并發,同樣的請求,這時,內存會邊用邊回收,不會飆了。在觀察各項性能指標時,發現一個問題,當并發變更時,tps始終是30來個,更奇怪的是,pod的資源有有空閑,數據庫的資源也有空閑(大數據查庫的方案在路中)。這不僅僅是一個問題,還是一個棘手的問題。
先看看代碼邏輯,就是在service層先查一下緩存,拿上信息再查數據庫,回來的數據進行重組封裝,緩存用的官方分布式Redis緩存組件,數據庫訪問用的dapper,按理這些組件都應該是靠得住的,自己寫的邏輯中到是有少許復雜。總感覺是那里有個鎖,卡住了,所有資源都還有剩余,但tps起不來。
在這時,生產環境報出了504錯誤,網關超時,說明上游請求有超時現象。
這時又增加了排查任務,504超時,結合上面的分析,覺得tps上不來,更多的請求等待,必然會有超時的,隱隱覺得是一個問題,只要疏通那個鎖,就應該能解決504超時。同時經過測試發現,只要一個大查詢,數據量足夠大,需要數據庫等待,其他查詢就會卡住,這肯定會有504出現。
繼續排查吧,現在只能把這個復雜的查詢分解開來測試,只進行緩存操作,沒問題;只進行大數據查詢,資源占用也能上來。那就是只要緩存組合慢的大查詢,就有問題。
為了驗證問題,把Redis分布緩存組件改成內存式分布式緩存(因為測試是在單pod上進行),再次測試時,發現問題解決了,數據庫的cpu就會升起來,pod的cpu也會升,最終以數據庫cpu打滿結束,這個時候,tps也升到是150多個,這就足以說明官方Redis分布緩存有坑(原因是緩存包是從舊的版本升上來的,官方已發布新版本)。
到此,我的分析工作就告一段落,內存和tps上升不了(504也一樣的原因)都找到問題了。接下來那就整改代碼,再度進行性能測試,驗證問題。
想在這里說的是,排查性能問題是個慢活,細活,通過現象推斷問題,有很多時間,現象會把人帶偏,沒關系,再通過更多的現象進行再推斷,再排查。另外,不要相信所有組件都是可靠的,有可能單個可靠,組合就有問題,一定要分段排查,完整驗證,再去信任。
這次排查后,可能還有其他性能問題,沒關系,那就再排查,一個好的服務性能,肯定是經過千錘百煉的,不可能是天生的。