記一次線上OOM和性能優化

某次週五,發佈前一週的服務器小動盪😶

事情回顧

上週五,經過Grafana監控,線上環境忽然出現CPU和內存飆升的狀況:git

可是看到網絡輸入和輸入流量都不是很高,因此網站被別人攻擊的機率不高,後來服務器負荷居高不下,只能保存dump文件進行分析,並一臺一臺服務器進行從新啓動(還好你們週五下班了)shell

經過日誌分析

既然服務器在某個時間點出現了高負荷,因而就先去找一開始出現問題的服務器,去找耗時的服務,例如我當時去找數據庫耗時的服務,因爲上週的日誌已經被刷掉,因而我大體描述一下:數據庫

[admin@xxx xxxyyyy]$ grep '15:14:' common-dal-digest.log |  grep -E '[0-9]{4,}ms'
2018-08-25 15:14:21,656 - [(xxxxMapper,getXXXListByParams,Y,1089ms)](traceId=5da451277e14418abf5eea18fd2b61bf)
複製代碼

上述語句是查詢在15:14那一分鐘內,在common-dal-digest.log文件中,耗時超過1000ms的SQL服務(我上週查的是耗時超過10秒的服務)。性能優化

經過traceId去查Nginx保存的訪問日誌,定位在該時間點內,分發到該服務器上的用戶請求。還有根據該traceId,定位到整個調用流程所使用到的服務,發現的確十分耗時...服務器

因而拿到了該請求具體信息,包括用戶的登陸手機號碼,由於這個時候,其它幾臺服務器也出現了CPU和內存負載升高,因而根據手機號查詢了其它幾臺服務器的訪問日誌,發現同一個請求,該用戶也調用了不少次...網絡

使用mat進行dump文件分析

經過mat工具對dump文件進行分析,調查是什麼請求佔用了大內存:app

觀察了該對象的引用樹,右鍵選擇【class_ reference】,查看對象列表,和觀察GC日誌,定位到具體的對象信息。工具

經過日誌以及dump文件,都指向了某個文件導出接口,接着在代碼中分析該接口具體調用鏈路,發現導出的數據不少,並且老代碼進行計算的邏輯嵌套了不少for循環,計算效率低。post

查詢了該用戶在這個接口的所調用的數據量,須要查詢三個表,而後for循環中大概會計算個100w+次,致使阻塞了其它請求,線上的服務器CPU和內存使用狀況一直飆升。性能


代碼進行性能優化

在看到該業務在git提交記錄是我上一年實習期寫的時候,個人心裏是崩潰的,當時對業務不熟悉,直接循環調用了老代碼,並且也沒有測試過這麼大的數據量,因此GG了。

而後我就開始作代碼性能優化,首先仔細梳理了一下整個業務流程,經過增長SQL查詢條件,減小數據庫IO和查詢數據量,優化判斷條件,減小for嵌套、循環次數和計算量。

經過jvisualVM進行對比

對比新老代碼所佔用的CPU和內存狀態

優化前:

優化後:

經過上述優化以後,計算1w條數據量進行導出,在老代碼須要48s,新代碼也要8s

,不過這是大數據量的狀況下,實際用戶的數據沒有這麼多,因此基本上知足了線上99%的用戶使用。

固然,因爲這些數據是本地開發環境新增長的,與出現OOM問題的用戶數據量還有些差異,但經過優化後的代碼,已經在數據庫查詢的時候就過濾掉不少無效的數據,在for循環計算前也加了過濾條件,因此真正計算起來起來就下降了不少計算量。

恩,本身優化好了,還要等測試爸爸們測試後纔敢上線,此次要瘋狂造數據


開發注意點

在開發一開始的時候,都沒有考慮到性能問題,想着知足需求就完成任務,但數據量一大起來,就有可能出現這些OOM問題,因此之後開發時,須要考慮一下幾點:

  • 梳理設計流程
  • 考慮是否有性能問題
  • 與產品經理商量控制查詢條件,減小查詢的範圍
  • 與數據庫交互時,減小無效的查詢,合併查詢和合並更新操做
  • 減小for循環,尤爲注意for循環嵌套問題!
  • 調用老代碼要看注意=-=

吐槽

果真每次看以前寫的代碼,都有種要si要si的感受

萬元預算玩新機 還有沈大媽的表情真好玩,直接拿來用了~

相關文章
相關標籤/搜索