點選上方“Java技術驛站”,選擇“置頂公眾號”。
有內涵、有價值的文章第一時間送達!
分頁效能分析
效能瓶頸
查詢偏移量過大的分頁會導致資料庫獲取資料效能低下,以MySQL為例:
SELECT * FROM t_order ORDER BY id LIMIT 1000000, 10
這句SQL會使得MySQL在無法利用索引的情況下跳過1000000條記錄後,再獲取10條記錄,其效能可想而知。而在分庫分表的情況下(假設分為2個庫),為了保證資料的正確性,SQL會改寫為:
SELECT * FROM t_order ORDER BY id LIMIT 0, 1000010
即將偏移量前的記錄全部取出,並僅獲取排序後的最後10條記錄。這會在資料庫本身就執行很慢的情況下,進一步加劇效能瓶頸。因為原SQL僅需要傳輸10條記錄至客戶端,而改寫之後的SQL則會傳輸1000010*2的記錄至客戶端。
Sharding-JDBC的最佳化
Sharding-JDBC進行了2個方面的最佳化。
首先,Sharding-JDBC採用流式處理 + 歸併排序的方式來避免記憶體的過量佔用。Sharding-JDBC的SQL改寫,不可避免的佔用了額外的頻寬,但並不會導致記憶體暴漲。 與直覺不同,大多數人認為Sharding-JDBC會將1000010*2記錄全部載入至記憶體,進而佔用大量記憶體而導致記憶體上限溢位。 但由於每個結果集的記錄是有序的,因此Sharding-JDBC每次比較僅獲取各個分片的當前結果集記錄,駐留在記憶體中的記錄僅為當前路由到的分片的結果集的當前遊標指向而已。 對於本身即有序的待排序物件,歸併排序的時間複雜度僅為O(n),效能損耗很小。
其次,Sharding-JDBC對僅落至單分片的查詢進行進一步最佳化。落至單分片查詢的請求並不需要改寫SQL也可以保證記錄的正確性,因此在此種情況下,Sharding-JDBC並未進行SQL改寫,從而達到節省頻寬的目的。
更好的分頁解決方案
由於LIMIT並不能透過索引查詢資料,因此如果可以保證ID的連續性,透過ID進行分頁是比較好的解決方案:
SELECT * FROM t_order WHERE id > 100000 AND id <= 100010 ORDER BY id
或透過記錄上次查詢結果的最後一條記錄的ID進行下一頁的查詢:
SELECT * FROM t_order WHERE id > 100000 LIMIT 10
摘自:sharding-jdbc使用指南☞分頁及子查詢
是否需要這種分頁
無論是 SELECT *FROM t_order ORDER BY id LIMIT 0,100010
或者 SELECT *FROM t_order WHERE id >100000LIMIT 10
,效能都一般般,後者只是稍微好點而已,但是由於LIMIT的存在,mysql都需要排序;
是否能從產品角度或者使用者習慣等方面解決或者避免這個問題?
-
使用者習慣結合產品需求解決方案:
比如我們以前有個每日TOP榜單需求,分析使用者行為一般不會無限制往下滑,即使有這種使用者,也是極少數,可以忽略。這樣的話,可以透過SQL
***LIMIT 300
只查詢10頁總計300個TOP應用,然後把這些資料以list結構儲存到redis中。這樣的話,使用者檢視每日TOP榜單只需透過LRANGE key start stop
從redis快取中取資料即可,且限制查詢的offset不允許超過300;
END