MySQL分库分页_MySQL分库分表的分页查询解决方案
問題的提出
我們知道,當我們的數據量達到一定數量時,需要將數據表進行水平拆分,從而滿足大量數據的存儲和查詢,保證系統的可用性,但同時會出現另外一個問題就是,如果業務要查詢“最近注冊的第3頁用戶”,該如何實現呢?單庫上,可以通過簡單的sql實現分頁查詢。
select * from t_user order by time limit 200,100
1
select *fromt_userorderbytimelimit200,100
分庫分表后變成兩個庫后,分庫依據是user_id,排序依據是time,單個分數據庫層失去了time排序的全局視野,如果同樣需要實現分頁查詢時該怎么辦呢?有什么比較好的MySQL分庫分表的分頁查詢解決方案呢?
全局視野法
正常來講,不管哪一個分庫的第3頁都不一定有全局第3頁的所有數據,例如一下三種情況:
情況一:兩個分庫按照時間排序,數據各占一半,則每頁取offset和limit的一般數據回來合并就可以了
情況二:所有數據都在一個庫上,則取一個庫的所有數據回來就可以了
情況三,那么一般情況是,每個分庫的數據數據是隨機的,但是一定是在全局offset=600之內
由于不清楚到底是哪種情況,所以必須每個庫都返回3頁數據,所得到的6頁數據在服務層進行內存排序,得到數據全局視野,再取第3頁數據,便能夠得到想要的全局分頁數據。
這種方法缺點是:當查詢的頁數增大時,每個分庫所需返回的數據也越來成倍增加,降低了查詢的性能
業務折中
第一種折中的方案是
對全局視野法的一種優化,即禁用制定頁數的分頁查詢,必須通過下一頁來實現分頁查詢的頁數跳轉,并且在每次查詢下一頁時將上一頁的最大排序字段的值帶上(這里就是時間time),這樣在每個分庫查詢數據時待上這個條件,可以優化查詢速率。
第二種折中的方案是
數據庫分庫-數據均衡原理
使用patition key進行分庫,在數據量較大,數據分布足夠隨機的情況下,各分庫所有非patition key屬性,在各個分庫上的數據分布,統計概率情況是一致的。
例如,在uid隨機的情況下,使用uid取模分兩庫,db0和db1:
(1)性別屬性,如果db0庫上的男性用戶占比70%,則db1上男性用戶占比也應為70%
(2)年齡屬性,如果db0庫上18-28歲少女用戶比例占比15%,則db1上少女用戶比例也應為15%
(3)時間屬性,如果db0庫上每天10:00之前登錄的用戶占比為20%,則db1上應該是相同的統計規律
利用這一原理,要查詢全局100頁數據,offset 9900 limit 100改寫為offset 4950 limit 50,每個分庫偏移4950(一半),獲取50條數據(半頁),得到的數據集的并集,基本能夠認為,是全局數據的offset 9900 limit 100的數據,當然,這一頁數據的精度,并不是精準的。
根據實際業務經驗,用戶都要查詢第100頁網頁、帖子、郵件的數據了,這一頁數據的精準性損失,業務上往往是可以接受的,但此時技術方案的復雜度便大大降低了,既不需要返回更多的數據,也不需要進行服務內存排序了。
二次查找法
有沒有一種方法既能滿足業務要求,并且不需要折中,性能還高的方法呢?
接下來介紹一種“二次查找法”,不知道能不能講的明白,我盡量吧。
為了方便舉例,假設一頁只有5條數據,查詢第200頁的SQL語句為select * from T order by time offset 1000 limit 5;
分五步:
1. 將select * from T order by time offset 1000 limit 5; 優化成select * from T order by time offset 500 limit 5,注意這里的500=1000/分表數量,并將這個sql下發至每個分庫分表中執行,每個分庫返回這個sql執行的結果。
2. 找到所有分庫返回結果的time的最小值
第一個庫,5條數據的time最小值是1487501123
第二個庫,5條數據的time最小值是1487501223
故,三頁數據中,time最小值來自第一個庫,time_min=1487501123,這個過程只需要比較各個分庫第一條數據,時間復雜度很低
3. 查詢二次改寫
第一次改寫的SQL語句是select * from T order by time offset 500 limit 5
第二次要改寫成一個between語句,between的起點是time_min,between的終點是原來每個分庫各自返回數據的最大值:
第一個分庫,第一次返回數據的最大值是1487501523
所以查詢改寫為select * from T order by time where time between time_min and 1487501523
第二個分庫,第一次返回數據的最大值是1487501699
所以查詢改寫為select * from T order by time where time between time_min and 1487501699
從上面圖片可以看出,DB1比第一次查出來的數據多了兩行,應為查詢的范圍擴大了
4. 計算time_min這條記錄在全局的offset
根據第一步查詢的sqlselect * from T order by time offset 500 limit ,我們知道每個庫的offset值了,將DB0中的最小time的數據虛擬到DB1中推算在DB1中的offset值=497
從而我們得知time_min這條記錄在全局的offset值=500+497=997
5. 根據第二次查詢出來的結果集,在內存中作排序,已知time_min在全局中的offset=997,那么結果集排序之后也能推算出offset=1000所在的記錄,從而獲得sqlselect * from T order by time offset 1000 limit 5的分頁查詢記錄(圖片黃色部分)
總結:可以精確的返回業務所需數據,每次返回的數據量都非常小,不會隨著翻頁增加數據的返回量。
瀏覽量:
7
0
總結
以上是生活随笔為你收集整理的MySQL分库分页_MySQL分库分表的分页查询解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python slice函数怎么取列表的
- 下一篇: 连接pgsql_Laravel 数据库连