MongoDB的分页范围很广

时间:2012-03-14 13:53:48

标签: performance mongodb pagination

据说使用skip()在具有许多记录的MongoDB集合中进行分页很慢,不推荐使用。

可以使用远程分页(基于> _id comparsion)

db.items.find({_id: {$gt: ObjectId('4f4a3ba2751e88780b000000')}});

显示上一页很好。 &安培;下一个按钮 - 但是当你想要显示实际的页码1 ... 5 6 7 ... 124时,它不是很容易实现 - 你需要预先计算每页开始的“_id”。

所以我有两个问题:

1)我应该什么时候开始担心?什么“记录太多”而跳过()的速度明显减慢? 1 000? 1 000 000?

2)使用远程分页时,使用实际页码显示链接的最佳方法是什么?

3 个答案:

答案 0 :(得分:95)

好问题!

“有多少人?” - 当然,这取决于您的数据大小和性能要求。当我跳过超过500-1000条记录时,我个人感到不舒服。

实际答案取决于您的要求。这是现代网站所做的事情(或者至少是其中的一些)。

首先,navbar看起来像这样:

1 2 3 ... 457

他们从总记录数和页面大小中获取最终页码。让我们跳转到第3页。这将涉及从第一条记录中跳过一些内容。当结果到达时,您知道第3页的第一条记录的ID。

1 2 3 4 5 ... 457

让我们再跳过一些,然后转到第5页。

1 ... 3 4 5 6 7 ... 457

你明白了。在每个点上,您可以看到第一页,最后一页和当前页,以及当前页面前后两页。

查询

var current_id; // id of first record on current page.

// go to page current+N
db.collection.find({_id: {$gte: current_id}}).
              skip(N * page_size).
              limit(page_size).
              sort({_id: 1});

// go to page current-N
// note that due to the nature of skipping back,
// this query will get you records in reverse order 
// (last records on the page being first in the resultset)
// You should reverse them in the app.
db.collection.find({_id: {$lt: current_id}}).
              skip((N-1)*page_size).
              limit(page_size).
              sort({_id: -1});

答案 1 :(得分:6)

很难给出一般答案,因为它很大程度上取决于您用于构建正在显示的结果集的查询(或查询)。如果只使用索引找到结果并以索引顺序显示,则db.dataset.find()。limit()。skip()即使有大量跳过也可以很好地执行。这可能是最简单的代码编写方法。但即使在这种情况下,如果您可以缓存页码并将它们绑定到索引值,例如,您可以使第二个和第三个想要查看第71页的人更快。

在一个非常动态的数据集中,当其他人正在翻阅数据时,将添加和删除文档,这样的缓存将很快变得过时,限制和跳过方法可能是唯一可靠的,以提供良好的结果

答案 2 :(得分:1)

我最近在尝试使用非唯一字段(例如“名字”)进行分页请求时遇到相同的问题。该查询的想法是能够在不使用skip()的情况下在非唯一字段上实现分页

这里的主要问题是能够查询不是唯一的“ FirstName”字段,因为会发生以下情况:

  1. $ gt:{“名字”:“卡洛斯”}->这将跳过名字为“卡洛斯”的所有记录
  2. $ gte:{“名字”:“卡洛斯”}->总是返回相同的数据集

因此,我想出的解决方案是通过将目标搜索字段与辅助字段组合在一起,使查询的$ match部分变得唯一,从而使其成为唯一搜索。

升序:

db.customers.aggregate([
    {$match: { $or: [ {$and: [{'FirstName': 'Carlos'}, {'_id': {$gt: ObjectId("some-object-id")}}]}, {'FirstName': {$gt: 'Carlos'}}]}},
    {$sort: {'FirstName': 1, '_id': 1}},
    {$limit: 10}
    ])

降序:

db.customers.aggregate([
    {$match: { $or: [ {$and: [{'FirstName': 'Carlos'}, {'_id': {$gt: ObjectId("some-object-id")}}]}, {'FirstName': {$lt: 'Carlos'}}]}},
    {$sort: {'FirstName': -1, '_id': 1}},
    {$limit: 10}
    ])

此查询的$ match部分基本上表现为if语句: 如果firstName是“ Carlos”,则它也必须大于此ID 如果firstName不等于“ Carlos”,则必须大于“ Carlos”

唯一的问题是您无法导航到特定的页码(这可以通过一些代码操作来完成),但是它解决了我对非唯一字段的分页的问题,而不必使用跳过很多的到达要查询的任何数据集结尾时的内存和处理能力。