MongoDB中的游标是什么?

Bai*_*Dev 23 cursor mongodb

我们cursor not found exceptions对一些Morphia Queries 的最终发生感到困扰asList,我发现了SO暗示,这可能是非常消耗内存的.

现在我想了解更多关于背景知识:可以用sombody解释(英文),Cursor(在MongoDB中)实际上是什么?为什么它可以保持打开或找不到?


文档游标定义为:

指向查询结果集的指针.客户端可以遍历游标以检索结果.默认情况下,游标在不活动10分钟后会超时

但这不是很有说服力.也许为定义batch查询结果可能会有所帮助,因为文档还指出:

MongoDB服务器批量返回查询结果.批量大小不会超过最大BSON文档大小.对于大多数查询,第一批返回101个文档或只有足够的文档超过1兆字节.后续批量大小为4兆字节.[...]对于包含没有索引的排序操作的查询,服务器必须加载内存中的所有文档以在返回任何结果之前执行排序.

注意:在我们的查询中,我们根本不使用sort语句,也不使用limitoffset.

xam*_*mir 34

在Node.js驱动程序中查找()和游标

var MongoClient = require('mongodb').MongoClient,
assert = require('assert');

MongoClient.connect('mongodb://localhost:27017/crunchbase', function (err, db) {
    assert.equal(err, null);
    console.log('Successfully connected to MongoDB.');

    const query = { category_code: "biotech" };

    // toArray() vs. cursor code goes here
});
Run Code Online (Sandbox Code Playgroud)

请注意,调用toArray()正在使应用程序获取整个数据集.

    db.collection('companies').find(query).toArray(function (err, docs) {
        assert.equal(err, null);
        assert.notEqual(docs.length, 0);

        docs.forEach(doc => {
            console.log(`${doc.name} is a ${doc.category_code} company.`);
        });

        db.close();
    });
Run Code Online (Sandbox Code Playgroud)

大段引用

请注意,光标通过返回的find()分配给toArray().使用这种方法,我们不是一次获取memort中的所有数据并一次使用数据,而是将数据流式传输到我们的应用程序.cursor.forEach()可以立即创建游标,因为在我们尝试使用它将提供的一些文档之前,它实际上并未向数据库发出请求.重点forEach()是描述我们的查询.第二个参数find()显示驱动程序耗尽或发生错误时要执行的操作.

在上面代码的初始版本中,它cursor强制数据库调用.这意味着我们需要所有文件,并希望它们在一个文件中cursor.forEach.

此外,toArray()以批处理格式返回数据.下图显示了游标(从应用程序)到的请求array

MongoDB游标请求

MongoDBMongoDB因为我们可以处理文件,直到我们到达终点.对比forEach- 我们等待检索所有文档并构建整个数组.这意味着我们没有从驱动程序和数据库系统一起工作以将结果批量处理到您的应用程序的事实中获得任何好处.批处理旨在提供内存开销和执行时间方面的效率.如果可以在您的应用程序中使用它,请充分利用它.

  • +1这正是我想知道的.我一直认为find()正在执行查询,而.limit和.toArray正在对该查询的结果进行操作.很棒的解释! (9认同)

pra*_*ad_ 11

该集合的find方法返回一个游标- 它指向与查询过滤器匹配的文档集(称为结果集)。结果集是查询返回的实际文档,但它位于数据库服务器上。

对于客户端程序,例如mongoshell,您会得到一个光标。您可以认为游标就像一个 API 或一个处理结果集的程序。游标有许多方法可以运行以对结果集执行某些操作。有些方法会影响结果集数据,有些方法会提供有关结果集的状态或信息。

由于游标维护有关结果集的信息,因此当您通过应用其他游标方法使用结果集数据时,某些信息可能会发生更改。您可以使用这些方法和信息来适应您的应用程序,即您想要如何处理查询的数据以及如何处理查询的数据。


使用游标及其一些常用的方法和mongoshell 功能来处理结果集:

count()方法最初返回结果集中文档数量的计数-作为查询的结果。它在游标生命周期的任何时刻始终保持不变。这是信息。即使游标关闭或耗尽后,此信息仍保持不变。

当您从结果集中读取文档时,结果集就会耗尽。一旦完全耗尽,你就无法再阅读了。告诉hasNext()是否有任何文档可供读取 - 返回布尔值 true 或 false。返回next()一个文档(如果可用)(您首先使用 进行检查hasNext,然后执行next)。这两种方法通常用于迭代结果集数据。另一种迭代方法是forEach().

数据是从服务器批量检索的- 具有默认大小。当您读取第一批文档时,当读取所有文档时,以下next()方法将检索下一批文档,依此类推,直到从结果集中读取所有文档。该批量大小可以配置,您还可以获取其状态。

如果您在光标上应用该toArray()方法,则结果集中的所有剩余文档都将加载到客户端计算机的内存中,并可作为 JavaScript数组使用。并且,结果集数据已耗尽。以下hasNext方法将返回false,并且next将抛出错误(一旦耗尽游标,就无法从中读取数据)。此方法将所有结果集数据加载到客户端内存(数组)中。如果结果集很大,这可能会消耗内存。

返回结果集中剩余文档的计数itcount()并耗尽游标。

有一些游标方法,例如isClosed()isExhausted()size()它们在您处理数据时提供有关游标及其基础结果集的状态信息。

这些是游标和结果集的基本特征。游标方法有很多种,您可以尝试看看它们是如何工作的并获得更好的理解。

参考


shell中的用法示例mongo

假设test集合有 200 个文档(以相同的顺序运行命令)。

  • var cur = db.test.find( { } ).limit(25)创建仅包含 25 个文档的结果集。
  • 但是,cur.count()将显示 200,这是查询过滤器的实际文档计数。
  • hasNext()将返回true
  • next()将返回一个文档。
  • itcount()将返回 24 (并耗尽光标)。
  • itcount()再次将返回0。
  • cur.count()仍会显示 200。


fro*_*rik 7

我不是一个mongodb专家,但我只是想在去年的中型mongo系统中加入一些观察结果.还要感谢@xameeramir关于游标如何工作的出色表现.

"游标丢失"异常的原因可能有几个.在这个答案中解释了我注意到的一个.

光标位于服务器端.它不是分布在副本集上,而是存在于创建时的主要实例上.这意味着如果另一个实例接管为主要,则游标将丢失给客户端.如果旧的主要设备仍在运行,它可能仍然在那里但是没有用.我想这是经过一段时间后被收集起来的.因此,如果您的mongo副本集不稳定或者前面有一个不稳定的网络,那么在执行任何长时间运行的查询时您都会运气不好.

如果光标想要返回的内容的完整内容不适合服务器上的内存,则查询可能非常慢.服务器上的RAM需要大于您运行的最大查询.

通过更好地设计可以部分避免所有这些.对于具有大型长时间运行查询的用例,使用几个较小的数据库集合而不是较大的数据库集合可能会更好.