1. 程式人生 > >mongodb 分頁查詢替代skip的方式

mongodb 分頁查詢替代skip的方式

以典型的列表api來說:下拉重新整理是獲取最新資訊,然後上拉載入下一頁

常見api要寫的2個介面

  • get_latest(model,count)
  • get_with_page(number,size)

get_latest一般是取最新的資料,比如我們常見的下拉重新整理,一般都是這樣的介面的。由於2次下拉之間,可能非常長的時間間隔,所以取到的資料會把當前列表的資料沖掉。

通常做法

  • 如果n(比如n=30s)分鐘內有連續請求,提示最近已更新,沒必要再刷,或者直接返回當前資料
  • 如果取到新資料,將當前列表的資料沖掉,保證資料一致性

如果判斷我到最後一頁了

常見的辦法是取出總數,除以pagesize,然後判斷當前頁是否和總頁數-1

n = all_count -1

量少的時候,毫無感覺,如果量大了,你去查一下count(*)是啥後果呢?

所以比較好的做法是按照id去查,前端根據每次返回的資料條數,如果條數等於pagesize,你就可以取下一頁資料,相反,如果取到的資料小於pagesize,你就知道沒有那麼多資料可以取了,即到了尾頁。此時只要disable獲取下一頁的按鈕即可。

使用 skip() 和 limit() 實現

//Page 1
db.users.find().limit (10)//Page 2
db.users.find().skip(10).limit(10)//Page 3
db.users.find().skip
(20).limit(10)........

抽象一下就是:檢索第n頁的程式碼應該是這樣的

db.users.find().skip(pagesize*(n-1)).limit(pagesize)

當然,這是假定在你在2次查詢之間沒有任何資料插入或刪除操作,你的系統能麼?

當然大部分oltp系統無法確定不更新,所以skip只是個玩具,沒太大用

而且skip+limit只適合小量資料,資料一多就卡死,哪怕你再怎麼加索引,優化,它的缺陷都那麼明顯。

如果你要處理大量資料集,你需要考慮別的方案的。

使用 find() 和 limit() 實現

之前用skip()方法沒辦法更好的處理大規模資料,所以我們得找一個skip的替代方案。

為此我們想平衡查詢,就考慮根據文件裡有的時間戳或者id

在這個例子中,我們會通過‘_id’來處理(用時間戳也一樣,看你設計的時候有沒有類似created_at這樣的欄位)。

‘_id’是mongodb ObjectID型別的,ObjectID 使用12 位元組的儲存空間,每個位元組兩位十六進位制數字,是一個24 位的字串,包括timestamp, machined, processid, counter 等。下面會有一節單獨講它是怎麼構成的,為啥它是唯一的。

使用_id實現分頁的大致思路如下

  1. 在當前頁內查出最後1條記錄的_id,記為last_id
  2. 把記下來的last_id,作為查詢條件,查出大於last_id的記錄作為下一頁的內容

這樣來說,是不是很簡單?

程式碼如下

//Page 1
db.users.find().limit(pageSize);//Find the id of the last document in this page
last_id =...//Page 2
users = db.users.find({'_id'> last_id}). limit(10);//Update the last id with the id of the last document in this page
last_id =...

這只是示範程式碼,我們來看一下在Robomongo 0.8.4客戶端裡如何寫

db.usermodels.find({'_id':{"$gt":ObjectId("55940ae59c39572851075bfd")}}).limit(20).sort({_id:-1})

根據上面介面說明,我們仍然要實現2個介面

  • get_latest(model,count)
  • get_next_page_with_last_id(last_id, size)