分頁身影多如狗
沒錯兒,這就是隨處可見的分頁技術!
需要訪問的訊息量太大了,如果一次都給你,那只能讓你feel到等女友化妝出門的感覺;
聰明的猿哥哥幫你想了個辦法,他不會一次性把所有訊息都給你,你只需稍等片刻就可以先看到一些猿哥哥覺得你比較關注的東西,如果覺得不夠,就再給你一些,也有可能你厭煩了猿哥哥的訊息,悻悻而去
換成sql語言,可以這麼表達一下:
select * from t order by id limit x, y
select * from t order by id limit y offset x
使用id欄位建立乙個排序索引,然後獲取跳過x條資料獲取接下來的y條資料
但真正到業務上實現的時候也許更加複雜:
分庫問題遍地走
簡單業務或資料了較小的業務表可能大概一般不需要關注分庫問題,但在大型網際網路架構平台上,為了應對複雜的龐大的資料衝擊以及訪問體驗, 單單提供更強勁的硬體是不行的(也收穫不大),往往要對資料處理邏輯在設計上做出優化。
例如,龐大的資料量進行水平切分,按照一定的邏輯分不到不同的資料庫或其他儲存載體中去。
提到分庫,逃不開「分庫依據」(partition key)這個概念,大部分的業務場景,都會選擇業務 主鍵id來處理這個問題。
這就涉及到了分庫演算法,大部分的做法就是使用id來做取模或者hash來保證資料均勻的分布到不同的儲存載體中去。
還是sql的例子:
select * from t order by id limit y offset x
分庫之後,這個問題發生了變化,因為id被hash到不同的庫表中去了,利用id進行全域性資料的查詢就失去了視野。
比如,我們有一些資料,暫且嘉定為:(1,2,3,4,5,6,7,8)
現在我們要取出2,3這兩個資料,只需要limit 1,2就行了
現在假設按2取模分成了兩段資料:(1,3,5,7)、(2,4,6,8)
(1,3,5,7)=> limit 1,2 =>(3,5)
(2,4,6,8)=> limit 1,2 =>(4,6)
然後記憶體合併排序後取前兩個的到了(3,4),不是我們想要的結果,有人說了,你不能仍然簡單的在分表中使用limit 1,2了。
是的,那這個問題就麻煩一些了,我是否應該先limit 0, 1+2呢?
(1,3,5,7)=> limit 0,3 =>(1,3,5)
(2,4,6,8)=> limit 0,3 =>(2,4,6)
然後記憶體中合併使用limit 1,2的到了(3,4),這樣倒是滿足了。
也就是說:每個庫都必須返回 x+y 個資料,所得到的 m*(x+y) 在服務層進行記憶體排序,然後再取總的偏移量x後的y條記錄。
但再想一想,如果我們的資料量仍然比較大,(1,2,…10億)
而我們可能需要的到10億-1這個數所在的頁,資源消耗是不是很大呢?
我們也可以看看業界對這個問題都是怎麼考慮的
關於如何解決這個問題,業界一般有下列幾種方案。
1. 全域性視野法
也就是我們上面討論的方法。
服務層通過id取模將資料分布到兩個庫上去之後,每個資料庫都失去了全域性視野,資料按照某個partition id區域性排序之後,不管哪個分庫的第n頁資料,都不一定是全域性排序的第n頁資料。極端情況下,甚至所有的結果資料只來自某乙個庫;也有可能兩個庫的資料分布及其不均衡,一些結果資料來自第乙個庫的後半段,而另一些資料來自另乙個庫的前半段。
由於不清楚到底是哪種情況,所以必須每個庫都返回n頁資料,所得到的2n頁資料在服務層進行記憶體排序,得到資料全域性視野,再取第n頁資料,便能夠得到想要的全域性分頁資料。
再總結一下這個方案的步驟:
方案優勢:
通過服務層修改sql語句,擴大資料召回量,能夠得到全域性視野,業務無損,精準返回所需資料。
方案缺點:
2. 業務折衷法
「全域性視野法」雖然效能較差,但其業務無損,資料精準,不失為一種方案,有沒有效能更優的方案呢?
「任何脫離業務的架構設計都是耍流氓」,技術方案需要折衷,在技術難度較大的情況下,業務需求的折衷能夠極大的簡化技術方案。
只能查第一頁,如果想要得到的資料不在兩個分表的第一頁,那就算了…
個人感覺神經大條的想法,但可能在某些場景也很實用, 比如限定查詢銷量排名前5的商品, 其他的不需要看;也可能根據需要最多再多指定一些頁面, 比如我通過谷歌瀏覽器查詢某個資訊, 當查到第三個頁的時候還沒有看到想要的資訊,那我就不再看了。
「全域性視野法」能夠返回業務無損的精確資料,在查詢頁數較大,例如第100頁時,會有效能問題,此時業務上是否能夠接受,返回的100頁不是精準的資料,而允許有一些資料偏差呢?
資料庫分庫-資料均衡原理
使用patition key進行分庫,在資料量較大,資料分布足夠隨機的情況下,各分庫所有非patition key屬性,在各個分庫上的資料分布,統計概率情況是一致的。
例如,在uid隨機的情況下,使用uid取模分兩庫,db0和db1:
性別屬性,如果db0庫上的男性使用者佔比70%,則db1上男性使用者佔比也應為70%
年齡屬性,如果db0庫上18-28歲少女使用者比例佔比15%,則db1上少女使用者比例也應為15%
時間屬性,如果db0庫上每天10:00之前登入的使用者佔比為20%,則db1上應該是相同的統計規律
利用這一原理,要查詢全域性100頁資料,offset 9900 limit 100改寫為offset 4950 limit 50,每個分庫偏移4950(一半),獲取50條資料(半頁),得到的資料集的並集,基本能夠認為,是全域性資料的offset 9900 limit 100的資料,當然,這一頁資料的精度,並不是精準的。
根據實際業務經驗,使用者都要查詢第100頁網頁、帖子、郵件的資料了,這一頁資料的精準性損失,業務上往往是可以接受的,但此時技術方案的複雜度便大大降低了,既不需要返回更多的資料,也不需要進行服務記憶體排序了。
這種情況下,只能limit 0, x;
3. 二次查詢法-推薦
假設一頁只有5條資料
假設原始表t的資料按照pid分成了3個分庫
假設要查詢第200個頁面, 也就是原有序表的offset為995的資料
步驟:
優點: 可以精確的返回業務所需資料, 每次返回的資料量都非常小, 不會隨著翻頁增加資料的返回量
不足: 需要進行兩次查詢, 但這個對於深度翻頁的時候效果基本上一定是優於全域性視野法的
4. 標記快取法
這種方法對於業務來說可能就比較死板了, 不能根據要求去事實的改變查詢策略, 也就是所謂的打標籤法, 乙個標籤記錄了乙個頁面資料或幾個頁面資料的儲存位址, 找到了這個標籤, 只能取回這個標籤內對應的內容。
實際上就是一切都是提前安排好的, 嚴格來說, 可能不屬於分頁查詢技術的範疇, 只是看起來像罷了。。
參考:
mysql分庫 分頁查詢
1.現在使用elasticsearch了。基於lucene的解決方案 2.必須將mysql裡的資料寫入到類似hbase這樣的分布式資料庫,查詢快。但分頁 查詢的話,可以在hbase前加一層solr,用於建立資料索引 yeyingsheng 發布於 2015 04 21 10 18 閱讀 7k 收藏 ...
分庫後如何高效的做分頁
現在連線6臺mysql資料庫,路由分庫演算法是根據id的hash值 6,根據值不同分別存入6臺資料庫。現在不知道如何做分頁查詢列表?先決原則 資料分庫時盡量選擇索引字段,最好此欄位還是唯一的。資料分庫時建表時結合業務,選擇where後查詢比較頻繁的字段。解決方案泛泛之談 1 直接使用跨庫的多表聯合查...
cobar and mysql分庫字段問題
今天調程式從資料庫裡取資料,一直去不出來,把日誌裡打出來的sql放到資料庫裡去執行,卻又能 取出資料來,很是鬱悶。資料庫是mysql的,一共128個庫,通過某個字段分庫。後來發現要查的這條資料的分庫欄位被改過,按照程式裡執行的去查,他就去了另外乙個庫去查詢了 因為分庫欄位被改過 而直接去資料庫裡去查...