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條記錄的_id,記為last_id
- 把記下來的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)