海量資料的分頁怎麼破

2021-09-26 07:27:03 字數 3564 閱讀 8271

分頁應該是極為常見的資料展現方式了,一般在資料集較大而無法在單個頁面中呈現時會採用分頁的方法。

各種前端ui元件在實現上也都會支援分頁的功能,而資料互動呈現所相應的後端系統、資料庫都對資料查詢的分頁提供了良好的支援。

以幾個流行的資料庫為例:

查詢表 t_data 第 2 頁的資料(假定每頁 5 條)

db.t_data.find().limit(5).skip(5);
儘管每種資料庫的語法不盡相同,通過一些開發框架封裝的介面,我們可以不需要熟悉這些差異。如 springdata 提供的分頁介面:

public inte***ce pagingandsortingrepositoryextends crudrepository
這樣看來,開發乙個分頁的查詢功能是非常簡單的。

然而萬事皆不可能盡全盡美,儘管上述的資料庫、開發框架提供了基礎的分頁能力,在面對日益增長的海量資料時卻難以應對,乙個明顯的問題就是查詢效能低下!

那麼,面對千萬級、億級甚至更多的資料集時,分頁功能該怎麼實現?

下面,我以 mongodb 作為背景來**幾種不同的做法。

按照這個做法的查詢方式,如下圖所示:

,

"winningplan" : ,

"indexname" : "_id_",

"ismultikey" : false,

"direction" : "backward",

"indexbounds" :

可以看到隨著頁碼的增大,skip 跳過的條目也會隨之變大,而這個操作是通過 cursor 的迭代器來實現的,對於cpu的消耗會比較明顯。

而當需要查詢的資料達到千萬級及以上時,會發現響應時間非常的長,可能會讓你幾乎無法接受!

或許,假如你的機器效能很差,在數十萬、百萬資料量時已經會出現瓶頸

既然傳統的分頁方案會產生 skip 大量資料的問題,那麼能否避免呢?答案是可以的。

改良的做法為:

選取乙個唯一有序的關鍵字段,比如 _id,作為翻頁的排序字段;

每次翻頁時以當前頁的最後一條資料_id值作為起點,將此併入查詢條件中。

如下圖所示:

修改後的語句執行計畫如下:

},"winningplan" : ,

"indexname" : "_id_",

"ismultikey" : false,

"direction" : "backward",

"indexbounds" :

可以看到,改良後的查詢操作直接避免了昂貴的 skip 階段,索引命中及掃瞄範圍也是非常合理的!

為了對比這兩種方案的效能差異,下面準備了一組測試資料。

測試方案

準備10w條資料,以每頁20條的引數從前往後翻頁,對比總體翻頁的時間消耗

db.articles.remove({});

var count = 100000;

var items = ;

for(var i=1; i<=count; i++) ;

items.push(item);

if(i%1000==0)

}

傳統翻頁指令碼

function turnpages(pagesize, pagetotal), ).sort().skip(currentpage*pagesize).limit(pagesize);

dl = list.toarray();

//沒有更多記錄

if(dl.length == 0)

currentpage ++;

//printjson(dl)

} var t2 = new date();

var spendseconds = number((t2-t1)/1000).tofixed(2)

print("turn pages: ", currentpage, "spend ", spendseconds, ".")

}

改良翻頁指令碼

function turnpagebyid(pagesize, pagetotal)}: {};

var list = db.articles.find(condition, ).sort().limit(pagesize);

dl = list.toarray();

//沒有更多記錄

if(dl.length == 0)

//記錄最後一條資料的id

currentid = dl[dl.length-1]._id;

} var t2 = new date();

var spendseconds = number((t2-t1)/1000).tofixed(2)

print("turn pages: ", currentpage, "spend ", spendseconds, ".")

}

以100、500、1000、3000頁數的樣本進行實測,結果如下

可見,當頁數越大(資料量越大)時,改良的翻頁效果提公升越明顯!

這種分頁方案其實採用的就是時間軸(timeline)的模式,實際應用場景也非常的廣,比如twitter、微博、朋友圈動態都可採用這樣的方式。

而同時除了上述的資料庫之外,hbase、elastisearch 在range query的實現上也支援這種模式。

時間軸(timeline)的模式通常是做成「載入更多」、上下翻頁這樣的形式,但無法自由的選擇某個頁碼。

那麼為了實現頁碼分頁,同時也避免傳統方案帶來的 skip 效能問題,我們可以採取一種折中的方案。

通常在資料量非常大的情況下,頁碼也會有很多,於是可以採用頁碼分組的方式。

以一段頁碼作為一組,每一組內資料的翻頁採用id 偏移量 + 少量的 skip 操作實現

具體的操作如下圖所示:

實現步驟

對頁碼進行分組(groupsize=8, pagesize=20),每組為8個頁碼;

提前查詢 end_offset,同時獲得本組頁碼數量:

db.articles.find( }).sort().skip(20*8).limit(1)

分頁資料查詢以本頁組 start_offset 作為起點,在有限的頁碼上翻頁(skip)

由於乙個分組的資料量通常很小(8*20=160),在分組內進行skip產生的代價會非常小,因此效能上可以得到保證。

隨著物聯網,大資料業務的白熱化,一般企業級系統的資料量也會呈現出快速的增長。而傳統的資料庫分頁方案在海量資料場景下很難滿足效能的要求。

在本文的**中,主要為海量資料的分頁提供了幾種常見的優化方案(以mongodb作為例項),並在效能上做了一些對比,旨在提供一些參考。

海量資料的分頁

第一種方法 declare pagesize int,currpage int,topnum int,previous int select pagesize 30 select currpage 2 select topnum currpage pagesize select previous c...

海量資料分頁

language vbscript codepage 936 分頁sql語句生成 function getpagesql tblname,fldname,pagesize,pageindex,ordertype,strwhere dim strtemp,strsql,strorder 根據排序方式生...

海量資料分頁查詢

select top 25 id,registerid,filenameid,createtime from tbfilenamerecord where id select min id from select top 100 id from tbfilenamerecord where regi...