現象
- 高併發請求,需要操作數據庫。大量的查詢、刪除、插入、更新操作。
- 一次請求需要處理10萬條數據左右,每條數據處理,都需要查詢、刪除、插入、更新多次,操作多張表。
- 爲了防止內存溢出,一次處理1000條。在Controller層方法分頁,處理在service層執行,這樣每處理1000條,提交一次。
- 程序初始運行很快,處理1000個大概10秒,後來逐步的越來越慢,逐步變大,變成幾十、幾百、直至一千多秒
- 年輕代GC頻繁,但是沒有STW,GC時間也不長,影響不大
- 重啓項目後,重新執行,還是這種情況。一開始很快,後來越來越慢
排查
- arthas
- jmap
結果
- 最終定位到hibernate內存泄漏
- FlushEntityEvent向上追蹤調用堆棧,是hibernate查詢方法
q.list()
- session沒有清空,越來越多,釋放不了
- 正常使用沒問題,但是高併發的時候,來不及釋放清空session,越用越慢
- 清空後可以,每1000條,7秒左右,直到處理完20萬左右數據,仍然是10秒左右的時間,符合業務需求
- 清空session對其他操作數據庫的方法執行有影響,只修改了本方法,處理一批次1000條後,清空一次。爲了排除對其他業務的影響,爲了這個數據處理方法,單獨部署一個服務。
- 在dao層添加方法,獲取當前session,並清空,先
flush
再clear
。
public void clearSession() {
Session session = this.getCurrentSession();
session.flush();
session.clear();
}