1. 問題背景
在項目開發中,我們需要實現一個復雜的分頁查詢功能,涉及大量 IP 地址數據的處理和多表關聯。在我接手這個項目的時候,代碼是這樣的
要知道代碼里面的 ipsList 數據可能幾萬條甚至更多,這樣拼接的sql,必然是要內存溢出的,一味地擴大jvm參數不是解決問題的根本
2.優化歷程
2.1.臨時表處理
為了解決內存溢出的問題,我嘗試使用臨時表,分批次處理ipsList數據
雖然解決了棧溢出的問題,但是數據量太大,頻繁的io,單次查詢的時間也大概在9秒左右,batchSize的值也不是越大越好,但是不管嘗試多少,單次查詢的時間最快也需要八秒多,如果我是用戶,我覺得這是不能忍受的,但是對于程序員來說,能跑就行,想要快,那是另外的價錢,不過誰讓我善良體貼又溫柔呢,于是分析了一下,耗費時間的這一步無非是overhauledPlanMapper.insertBatchWithParams(params);
這個io操作,如果能異步并發處理的話,是不是就能解決查詢慢的問題了,畢竟這也不涉及到共享變量的修改
(寫到這里,手有些涼,于是插進了口袋,糟糕,早上買的兩個雞蛋忘記吃了,這一天天的上班把我腦子都上壞了)
2.2異步并發編程
到這里我覺得已經很完美的解決了這個問題了,等我部署上去運行的時候
我很無解,仔細研究了代碼,我不明白為什么會出現臨時表不存在的問題,看了日志我發現在insert語句還沒有完成之前表就已經被drop了,一開始我以為是線程安全問題,于是我開始嘗試加鎖,使用synchronized(TEMP_TABLE_LOCK)全局鎖,使用事務,確保所有操作在同一個事務中進行,但是不管我使用哪種方法,依然會存在臨時表不存在的問題,這讓我很百思不得其解.
在翻閱了很多資料之后我終于發現了問題所在,問題就出在臨時表上,讓我們看看臨時表的特點
生命周期:
-
臨時表(TEMPORARY TABLE):
-
僅在當前會話(Session)可見
-
會話結束時自動刪除
-
不同會話間互不可見
可見性:
- 臨時表: 只對創建它的會話可見
看到這我終于明白了為什么會出現這個問題了,我得表是在主線程創建的,由于 臨時表僅在當前會話可見,不同會話間不能共享,所以子線程在并發插入的時候無法訪問主線程創建的表,我悟了,但是我又觸底反彈了,舔狗的劇本里舔狗才是主角,額…不好意思,走錯片場了.
到這問題就簡單了,我只需要把臨時表修改成普通表就行了,只需要刪除TEMPORARY關鍵字就行
到這里大功告成,已經完美解決了臨時表不存在的問題,部署運行,嘗試了多個batchSize的值,最終發現當batchSize=1000左右的時候,查詢效率最高,單次查詢時間在1.8秒左右
但是如果batchSize設置成固定的值的話,我覺得可能會出現個問題,如果ipsList的數量太大,就是批次太多,可能會有上百個批次,也就意味著可能會出現同時并發上百個線程,而你的cpu又不能同時負擔這么多線程的話,就會出現線程阻塞,服務就會卡死,于是再優化一波
這是最終的版本,寫注釋呢并不是給我看,我是怕后面接手的人看不懂,畢竟每次改別人的代碼我都是邊罵邊改的,口吐芬芳,鳥語花香,如芒刺背,如坐針氈,如鯁在喉…
雖然說這只是一個簡單的查詢,但是這中間優化的過程還是挺有意思的,思想和邏輯可以運用到其他項目中的各個業務中,對我的啟發還是挺大的,所以記錄一下,此篇文章為中午休息時間所寫,以此共勉…