1. 程式人生 > >效能優化(四)高CPU利用率

效能優化(四)高CPU利用率

還是上一篇中提到過的API,通過調查我們發現這個API在需要返回的資料量比較大的有些單次呼叫中CPU可能達到90%到100%。所以即便是呼叫量不大的情況下,這個API一樣是造成整個系統性能低下的最大貢獻者。其中有一次呼叫返回時間更是長達10s。其實CPU的使用率高的話,響應時間自然就長,需要很長時間的計算才可以返回。

優化的思路還是把單個查詢分成多次,降低每次返回的資料集的大小。資料量比較大的表最好是單獨查詢,不要去和其他表join。比如一張表中我們查出的結果集是10k,和另外一個只有20的結果集相乘就是幾十萬了,隨便再jion一兩個表就上百萬了。凡是上萬的結果集我都不太放心,更不要提10萬,百萬。另外一個好處是,單獨查這一張表的時候不用去LEFT JOIN,INNER JOIN可以解決所有問題,而且JOIN後面的表都是unique的record。有一個查詢的結果集特別大,通過分析後我認為是因為查詢的時間範圍很廣,有13個月的結果。為了進一步讓單個查詢的結果集變小,決定用時間段來分割。比如分成兩次查詢,各查半年左右。總之我的原則就是單次查詢的結果集要小,這樣給MyBatis去處理結果集的壓力就小。通過對每次查詢列印執行時間可以看出來,結果集大的時候返回特別慢,而且CPU也很高,通過工具可以看到ResultSet的getLong,getString等方法呼叫次數很多。如果還有很多typeHandler那給Mybatis的壓力就更大了。

這樣分了之後單次查詢返回的時間很快。但是問題就是怎麼把這些資料合併起來。因為是樹狀結構的資料,相當於從根部要獲得葉子。給你一個“師”的ID,需要查出所有他小面的各級單位的各種資料。一個SQL join到底的結果就是結果集很大,我的做法是一個SQL查出所有“旅”,一個SQL再查出所有“團”,依次類推。如何把這些資料合併呢?一級一級去迴圈遍歷的話必然是一個多層的迴圈。想象一個5層的迴圈,沒有哪一個程式設計師願意這樣寫。我的解決方案是用HashMap,這就是Hash的好處,定址很快,不用遍歷去找。這樣的話避免了多層迴圈,多層迴圈的時間複雜度應該是O(n*m*L....),這樣改寫之後至少可以變成O(n+m+l....)。還是空間換時間的做法,結果也可想而知,就是產生的臨時變數很多,GC很頻繁,minor GC很頻繁。效能測試之後發現,當TPS到100左右的時候CPU的GC佔比有時會有10%左右。但是暫時沒有想到更好的辦法,目前也還在這個專案可以接受的範圍。希望以後能想到更好的辦法。不過更好的辦法可能是改寫需求,讓這個API不要一次性返回那麼多資料,也就沒有挑戰性了。。