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