日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

分库后如何分页

發布時間:2024/8/23 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分库后如何分页 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在實際應用中, 為了降低單表的數據量, 會對較大的表進行水平切分, 將單表的數據切分到多表多庫中.

既然要切分, 就要有一個切分的依據, 比如說按照 ID 取模等. 那么多張表聯合分頁是如何做到的呢?

如果分表的依據是字段 A, 但是需要根據字段 B 進行分頁查詢, 針對這種情況應該如何處理呢?

為了后面方便說明, 這里舉個例子.

有一個文章表 user_article

其中有一個文章的發表時間 publish_date. 這個時間用戶是可以修改的.

按照 ID 取模分到了兩個表中.

user_article_1 user_article_0

現在有這樣一個需求:

按照文章的發表時間進行排序分頁

單表

先來看在單表的時候, 我們是如何查詢的, 之后再擴展到多表.

select * from `user_article` order by `publish_date` offset 0 limit 10;

單表查詢很簡單, 一條 sql 搞定.

多表

對于多表的情況, 將其抽象一下, 就是有兩個有序列表:

[1, 5, 7, 8, 9]
[2, 3, 4, 6, 10]

現在要取合并后的有序列表第 n 頁的數據.

方案一

想到的第一個方案, 將兩個列表合并之后, 就可以退化為單列表的情況了.

對應到 sql查詢中, 如果要取第三頁的數據, 可以肯定的是, 每個列表都不會讀到第四頁, 所以我們可以將每個表的前三頁拿出來, 在內存中進行合并后, 就可以拿到全局的第三頁了.

# 取第三頁的數據 select * from `user_article_0` order by `publish_date` offset 0 limit 3*10; select * from `user_article_1` order by `publish_date` offset 0 limit 3*10;

這種方案確實可以獲取到分頁的數據, 但是查詢的數據量隨著頁數的增大而增大. 如果查詢第200頁的數據, 那每張表都要返回2000條數據, 性能太低.

方案二

如果說, 我們按照頁數依次獲取, 中間不跳頁的話, 那么就可以將上一次查詢的終點保存下來, 供下一次查詢使用.

比如, 上一次查詢, 最后一條數據是8, 那么, 下一次查詢從各個列表中取出大于8的10條數據, 內存排序后取前10條, 同時將最后一條的值存下來供下一次查詢使用.

對應到sql查詢中, 就需要有一個全局的searchId.

select * from `user_article_0` where `publish_date` > 'searchId' order by `publish_date` limit 10; select * from `user_article_1` where `publish_date` > 'searchId' order by `publish_date` limit 10;

每頁查詢數量固定, 效率較高. 但同時, 這種功能方案不能做到跳頁, 如果要查詢第 n 頁的數據, 前提是查詢了 n-1頁. 同時, 此方案要求 publish_date字段無重復值, 如果有20條相同的publish_date, 在翻頁的時候就會丟失部分數據.

方案三

那么, 有沒有一種方案, 即能跳頁查詢, 查詢數量也能保持在常量級呢?

說明

來了, 為了方便說明, 先從數組開始:

[1, 3, 5, 7, 11, 18, 23, 32, 41]
[2, 8, 9, 15, 17, 22, 27, 51, 60]

此時, 如果想獲取全局: offset 4 limit 4 的數據. 因為我們不知道全局偏移量4在各個數組中的各自偏移量. 所以在方案一中需要進行大量的查詢, 如果我們知道了, 問題不就解決了么.

第一步, 分別取各列表半個偏移量的數據

先分別取各個數據中offset 2 limit 4的數據. 結果如下:

[5, 7, 11, 18]
[9, 15, 17, 22]

注意: 這里的offset 2, 是通過全局偏移量/表個數算出來的.

拿到這個數據之后, 我們得到了什么? 其中的最小值5, 全局偏移量必定>=2.

  • 如果數據1中小于5的值大于2個, 那么第一次查詢時結果必定不同
  • 如果數據2中存在小于5的值, 那么5的全局偏移量只會增加.

第二步, 獲取最小值的全局偏移量

通過第一步的分析, 如果我們能夠知道數據2中存在多少個小于5的值, 那么我們就能夠計算出5的全局偏移量. 進而得到全局偏移量為4的數據.

查詢數據2中, data > 5 and data < 9的數據, 結果如下:

[8]

前面, 我們知道9在數據2中的偏移量為2. 同時 數據2中, >5 and <9的數據, 個數為1, 計算可得, 數據2中小于5的數據個數為: 2-1=1

又因, 5在數據1中的偏移量為2, 進而可得, 5的全局偏移量為: 2+1=3. (加上數據2中小于5的數量)

第三步, 整合數據并返回

我們將前后查詢的結果整合一下, 得到如下數據:

[5, 7, 11, 18]
[8, 9, 15, 17, 22]

再將兩數組合并為一個數組:

[5, 7, 8, 9, 11, 15, 17, 18, 22]

已知, 5的全局偏移量為3, 則偏移量為4的數據為7. 我們從7開始, 向后拿4個, 就是全局的offset 4 limit 4的數據了.

[7, 8, 9, 11]

問題

到這里, 你以為已經完成了么? 看下面這組數據:

[1, 2, 3, 4, 5, 6, 7, 8]

[9, 10, 11, 12, 13, 14, 15, 16]

很明顯, 這組數據分布十分不均勻, 按照上面的操作獲取分頁數據offset 4 limit 4

第一步折半查詢結果offset 2 limit 4

[3, 4, 5, 6]

[11, 12, 13, 14]

然后拿到3的全局偏移量2. 得到偏移量4的數據為5. 組合后返回結果為:

[5, 6, 9, 10]

這, 明眼人一看, 就知道結果應該是[5, 6, 7, 8].

很明顯, 因為數據都在一張表上, 所以導致第一次獲取數據沒有取完. 但是, 到這一步, 我們獲取到的偏移量是沒有問題的.

也就是說, 全局偏移量為4的數據為5. 那么, 我們就可以退回到方案二 進行處理了.

當然, 如果對數據的精度要求沒有那么高, 或者確信數據分布不會出現這種極限情況, 可以忽略.

貌似網上將這種方法稱為二次查詢, 沒有找到文章提到這個問題, 難道說實際應用中不會遇到么?

sql

將上面方案轉為 sql, 取第4頁的數據, 既offset 30 limit 10

第一步, 分別取各表數據

select * from `user_article_0` order by `publish_date` offset 30/2 limit 10; select * from `user_article_1` order by `publish_date` offset 30/2 limit 10;

我們在這一步, 統計出查詢結果的最小值 M

第二步, 最小值全局偏移量

下方sql中的 M', 代表各個查詢結果的最小值.

select * from `user_article_0` where `publish_date`>M and `publish_date`<"M'" order by `publish_date`; select * from `user_article_1` where `publish_date`>M and `publish_date`<"M'" order by `publish_date`;

此時, 通過計算, 我們已經得到M的全局偏移量了. 同時, 也拿到了偏移量30的值S.

如果數據分布十分不均勻, 在這一步, 極端情況會將前面所有數據都拿出來.

第三步, 返回結果

如果確信不會出現前面提到的極限情況, 這里直接組合結果并返回即可.

否則, 繼續執行下面查詢并返回. 此時, 排序字段不可重復.

select * from user_article_0 where publish_date > S order by publish_date limit 10; select * from user_article_1 where publish_date > S order by publish_date limit 10;

此方案執行了三倍的數據庫查詢, 但優點是查詢效率恒定與頁數無關, 且支持跳頁.

方案四

因為我們的數據是按照 ID 取模, 根據概率分布, 兩個庫的數據分布應該是一樣的. 那是不是可以每個表取半頁數據, 拿回來拼上.

這樣的話, 取第3頁數據的 sql為:

select * from user_article_0 order by publish_date offset 20/2 limit 5; select * from user_article_1 order by publish_date offset 20/2 limit 5;

這樣的話, 數據不太準確, 拿到的數據順序可能不對, 但重點是查詢效率高啊. 應該是有對順序精度沒什么要求的場景吧. 想到了這種方案, 但是暫時沒有想到應用場景.

如果是針對分表字段排序的話, 那么數據分布均勻, 此方案完美.

最后

具體業務應該如何選擇分頁方式呢?

  • 如果不需要跳頁, 直接選擇方案二
  • 如果對順序精度沒什么要求, 直接選擇方案四
  • 如果只需要查詢前 n 頁數據, 且 n 比較小. 那么方案一 可能更適合你
  • 如果需要查詢所有數據, 且需要跳頁, 可以選擇 方案一 和 方案三 結合的方式. 優先選擇當前效率更高的.
    • 對于訪問頻率較高的前幾頁數據, 選擇 方案一
    • 對于頁數比較大的數據, 選擇 方案三

當然了, 前面說的情況, 排序字段與分表字段不同, 數據分布可能不均勻. 如果是相同的字段, 那就沒這么多事了, 數據都是均勻分布的, 參考 方案四

最后, 對于排序使用的字段, 最好能夠保證其唯一性, 如果不能, order by的時候, 請添加輔助字段排序.

總結

以上是生活随笔為你收集整理的分库后如何分页的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。