2016-04-21 68 views
6

我们对最终发生的cursor not found exceptions感到困扰,对于一些Morphia查询asList,我发现hint on SO,这可能是相当消耗记忆。什么是MongoDB中的光标?

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


文档defines一个光标为:

的指针到结果集的查询的。客户端可以迭代游标来检索结果。默认情况下,游标在闲置10分钟后超时

但这不是很明显。也许这可能是有帮助的定义查询结果的batch,因为docu also states

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

注意:在我们的查询中,我们根本不使用排序语句,也没有limitoffset

+0

我觉得你的问题是在你应该问的话题上徘徊。如果你确实收到*“光标找不到”*异常,那么故障问题将是你实施的代码。关于游标“实际是什么”的更广泛的讨论更多的是“更广泛的设计问题”,而不是解决特定编程问题的东西,比如本网站的用途,因此*“对游标过度咆哮,代码不够这可能会导致问题“*。就目前而言,你的问题看起来像是在要求一篇论文解释游标是什么。只需显示一些代码。 –

+0

问题是,这些例外的发生取决于环境。我们目前不知道,哪个参数(内存,CPU,什么)是至关重要的参数。所以我对一些**背景感兴趣**。我们的代码看起来像'ds.find(Translation.class).asList()'(ds是Morphia.Datastrore)。 – BairDev

+2

如果您在副本集中运行mongo,则如果您的服务器决定在另一个主节点上,则光标将会丢失。 – froderik

回答

2

我并不是指mongodb专家,但我只是想补充一些在去年工作在中型mongo系统中的观察结果。还要感谢@xameeramir提供了关于游标如何工作的出色步骤。

“光标丢失”异常的原因可能有几种。这个答案解释了我注意到的一点。

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

如果光标想要返回的内容的全部内容不适合服务器上的内存,查询可能会非常缓慢。您服务器上的RAM需要大于您运行的最大查询。

所有这一切都可以通过设计得更好来部分避免。对于大型长时间运行查询的用例,您可能更适合使用几个较小的数据库集合,而不是较大的数据库集合。

6

find()方法和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."); 

    var query = { 
    "category_code": "biotech" 
    }; 

    db.collection('companies').find(query).toArray(function(err, docs) { 

    assert.equal(err, null); 
    assert.notEqual(docs.length, 0); 

    docs.forEach(function(doc) { 
     console.log(doc.name + " is a " + doc.category_code + " company."); 
     }); 

     db.close(); 

     }); 

     }); 
     
    

注意,通话.toArray正在为获取整个数据集的应用程序。

 

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."); 

    var query = {"category_code": "biotech"}; 

    var cursor = db.collection('companies').find(query); 

     function(doc) { 
      cursor.forEach(
      console.log(doc.name + " is a " + doc.category_code + " company."); 
     }, 
     function(err) { 
      assert.equal(err, null); 
      return db.close(); 
     } 
    ); 

}); 
 

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

在上述代码的初始版本中,它是toArray(),它强制数据库调用。这意味着我们需要所有的文件,并希望他们在array

另外,MongoDB以批处理格式返回数据。下面显示的图像,从游标请求(从应用程序)MongoDB

MongoDB cursor requests

forEachtoArray更好,因为我们可以处理文档,因为他们进来,直到我们到达终点。将其与toArray对比 - 我们在那里等待全部要检索的文件和整个数组已建成。这意味着我们没有从驱动程序和数据库系统一起工作将结果批量分发到应用程序的事实中获得任何优势。批处理意味着提供内存开销和执行时间方面的效率。 充分利用它,如果你可以在你的应用

+0

+1这正是我想知道的。我一直认为find()正在执行查询,并且.limit和.toArray正在对该查询的结果进行操作。很好的解释! – coblr