我在Mongo的一个集合中有超过300k的记录。
当我运行这个非常简单的查询时:
db.myCollection.find().limit(5);
只需几毫秒。
但是当我在查询中使用skip时:
db.myCollection.find().skip(200000).limit(5)
它不会返回任何内容......它会运行几分钟而不返回任何内容。
如何让它变得更好?
答案 0 :(得分:87)
解决此问题的一种方法是,如果您有大量文档并且以排序顺序显示它们(如果您不是,我不确定skip
有多大用处)将使用您正在排序的键来选择下一页结果。
所以,如果你从
开始db.myCollection.find().limit(100).sort({created_date:true});
然后将光标返回的 last 文档的创建日期提取到变量max_created_date_from_last_result
中,您可以获得更高效的下一页(假设您有一个索引)在created_date
)查询
db.myCollection.find({created_date : { $gt : max_created_date_from_last_result } }).limit(100).sort({created_date:true});
答案 1 :(得分:65)
来自MongoDB documentation:
分页费用
不幸的是,跳过可能(非常)昂贵,并且要求服务器从集合或索引的开头走,以便在它开始返回数据页面(限制)之前到达偏移/跳过位置。随着页面数量的增加,跳过将变得更慢,更加cpu密集,并且可能IO绑定,具有更大的集合。
基于范围的分页可以更好地使用索引,但不允许您轻松跳转到特定页面。
你必须问自己一个问题:你多久需要40000页?另请参阅this文章;
答案 2 :(得分:2)
我发现将两个概念结合在一起(跳过+限制和查找+限制)具有很高的表现力。当您有很多文档(尤其是较大的文档)时,skip + limit的问题是性能不佳。 find + limit的问题是您无法跳转到任意页面。我希望能够不按顺序进行分页。
我要采取的步骤是:
如果我想获取16记录的第5432页(使用javascript),它看起来大致像这样:
let page = 5432;
let page_size = 16;
let skip_size = page * page_size;
let retval = await db.collection(...).find().sort({ "_id": 1 }).project({ "_id": 1 }).skip(skip_size).limit(1).toArray();
let start_id = retval[0].id;
retval = await db.collection(...).find({ "_id": { "$gte": new mongo.ObjectID(start_id) } }).sort({ "_id": 1 }).project(...).limit(page_size).toArray();
之所以可行,是因为即使您要跳过数百万条记录(我正在执行的操作),对计划索引的跳过也非常快。如果您运行explain("executionStats")
,则totalDocsExamined
的数量仍然很大,但是由于索引上的投影,它的运行速度非常快(实际上,从不检查数据块)。然后,使用手头页面开始处的值,您可以非常快速地获取下一页。
答案 3 :(得分:2)
我连接了两个答案。
问题是,当您使用跳过和限制而不进行排序时,它只是按照表的顺序分页,与将数据写入表的顺序相同,因此引擎需要创建第一个临时索引。最好使用现成的_id索引:)您需要使用_id排序。像这样的大桌子很快就过去了。
db.myCollection.find().skip(4000000).limit(1).sort({ "_id": 1 });
在PHP中将是
$manager = new \MongoDB\Driver\Manager("mongodb://localhost:27017", []);
$options = [
'sort' => array('_id' => 1),
'limit' => $limit,
'skip' => $skip,
];
$where = [];
$query = new \MongoDB\Driver\Query($where, $options );
$get = $manager->executeQuery("namedb.namecollection", $query);