MongoDB中游标的深入学习

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中非常重要的一种机制,使用游标对于提高查询效率是至关重要的。

数据库标签