MongoDB中游标的深入学习
1. 游标的概念
在MongoDB中,游标是用来遍历大量数据的一个机制。当查询MongoDB集合时,当数据量较大的时候,MongoDB并不会将所有数据一次性返回给客户端,而是采用游标获取数据的方式。
下面为大家介绍如何在MongoDB中使用游标,以及游标的那些特点:
1.1 游标基本语法
在MongoDB中使用游标进行查询时,我们需要按照以下的基本语法进行操作。
var cursor = db.collection_name.find(query, projection);
while(cursor.hasNext()) {
var data = cursor.next();
printjson(data);
}
上面的示例中,游标通过find()方法查询MongoDB集合中的数据并返回一个游标对象,然后在while循环中一次迭代取出一条记录,直到游标迭代完整个结果集。在这里有一些需要注意的点,如下:
游标在执行find()方法时不会返回所查询集合的数据,而是返回查询结果中的第一批数据。每次使用next()方法迭代将返回下一条记录。
游标的默认批量大小是20条。
可以使用sort()对查询结果进行排序,limit()限制返回的结果集大小,skip()分页处理,以及projection控制返回数据的字段。
如果数据量很大,记得使用游标关闭方法.close()释放资源。
1.2 游标执行的顺序
MongoDB中的游标会在查询之前计算出查询计划,然后将它们发送到数据库服务器,以便在服务器端执行。查询计划在一定程度上反映了查询的执行顺序。
查询计划的目标是尽可能快地返回结果,而不是按照某种特定的执行方式处理查询。查询计划通常包括以下步骤:
索引扫描:如果可以使用索引直接定位到所需数据,则查询计划将在此处终止,并且将这些数据返回给用户。
筛选文件:如果不存在适当的索引或者由于其他原因无法使用索引,则查询计划需要全面扫描数据库中的所有文档。在扫描文件的时候,服务器会选择与查询条件匹配的文档。
排序:根据语句表达中的值进行排序。
限制:调整所需文件数量(例如,使用skip()和limit())。
在确定查询语句的执行计划时,MongoDB尝试使用最简单、最有效、最能利用索引的方法来执行查询。您可以使用.explain()方法或运行MongoDB shell中的db.system.profile.find()来查看执行计划的详细信息。
2. 游标的高级使用
2.1 游标的超时
当数据文件处于打开状态时,MongoDB会尝试定期清理未使用的游标、客户端和Connection资源。这是避免资源泄漏的很好方法,但它也有一个副作用:如果查询时间太长,则可能会使游标在MongoDB上“超时”。
默认情况下,MongoDB会在10分钟内关闭未使用的游标,以保留内存。这个时间可以通过cursorTimeoutMillis选项进行设置。在代码中可以使用以下方法指定游标的超时。:
var cursor = db.collection.find().addOption(CursorOption.NO_CURSOR_TIMEOUT);
2.2 手动关闭游标
正如我们在前面的例子中所见,游标是可以在完成相应的查询时手动关闭的。但是,MongoDB特别容易在回调中引起意外的问题。
比方说,假设您有一个查询一次返回100000行的查询。这个查询不能一次性执行,而是需要分成几个批。
每个节点分别返回它拥有的批。
下面的代码演示了一个这样的场景。它每次请求1000行,并在请求行时手动关闭游标:
function findWithCursor () {
var cursor = db.collection.find();
while (cursor.hasNext()) {
var result = cursor.next();
//处理行
if (rowNum % 1000 === 0 ) {
cursor.close();
cursor = db.collection.find({ _id: { $gt: objectId } });
}
}
cursor.close();
}
这个做法带来了两个问题:
保证很麻烦:如果没有显示处理,任何未消耗完的结果都会浪费服务器资源。
错误处理困难:由于查询在回调中完成,因此当线程关闭时,无法通过catch块进行错误处理。
为避免这个问题,请看如下代码,其中使用finally中清理的逻辑:
function findWithCursorFiltered () {
var cursor = db.collection.find(
{ zip: "94040" },
{
_id: 0,
name: 1,
zip: 1
}
);
try {
while (cursor.hasNext()) {
var curZip = cursor.next();
print(curZip.name + " lives in zip code " + curZip.zip);
}
} finally {
cursor.close();
}
}
2.3 游标的批量大小
作为一种处理查询的机制,游标会永久地保留与查询关联的一些元数据。这些元数据占用内存,因此不对无限制批量大小越高。
在查询数据时,MongoDB默认会将每个游标批量大小设置为20条。可以使用.batchSize()方法更改这个值。
如果计划立即提取游标中的所有数据,则将批量大小设置为较高的值非常有利,这将减少因短暂空闲时间而使游标过早地超时的可能性。
var cursor=db.collection_name.find().batchSize(100);
2.4 游标的跳过和限制
可以使用skip()和limit()方法控制游标返回结果中的条目数量。
例如,假设你有一个包含10000条数据的集合,并且想要查询数据但只返回1000条,你可以这样进行控制:
var cursor = db.collection.aggregate([
{ $match : { type : "food" } },
{ $sort: { name: 1 } },
{ $limit : 100 },
{ $skip : 900 }
]);
在上面的代码中:
$match类型的阶段将仅匹配“type”为“food”的文档。
$sort排序所有匹配到的行。
将返回排序后的前100个条目。
返回结果集的第901个到1000个条目。
总结
通过本篇文章的学习,我们对MongoDB中的游标有了更深入的了解。我们知道了如何使用游标以及游标的特点,还了解了游标的超时、游标的批量大小以及游标的跳过和限制设置等重点内容。作为MongoDB中非常重要的一种机制,使用游标对于提高查询效率是至关重要的。