具有许多文档和负载的猫鼬游标

Dav*_*d J 1 mongoose mongodb node.js express

我们一直在 Node.Js/Express 中使用 mongoose 一段时间,我们不清楚的一件事是,当您使用 find 进行查询并且您有大量文档结果集时会发生什么。例如,假设您想遍历所有用户以进行一些低优先级的后台处理。

let cursor = User.find({}).cursor();
cursor.on('data',function(user) {
   // do some processing here 
});
Run Code Online (Sandbox Code Playgroud)

我的理解是 cursor.on('data') 不会阻塞。因此,如果您有 100,000 个用户,那么您将几乎同时处理 100,000 人的系统淹没。似乎没有“下一步”或其他方法来规范我们使用文档的能力。

您如何处理大型文档结果集?

Dar*_*tel 11

我刚刚找到了一种使用等待的“现代”方式。

for await (const doc of User.find().cursor()) {
  console.log(doc.name);
}
Run Code Online (Sandbox Code Playgroud)

我在一个集合中使用它来处理 400 万以上的文档,它对我来说效果很好。

如果您想参考,这里是 mongoose文档。


B. *_*ing 9

Mongoose 实际上确实有一种.next()用于游标的方法!查看猫鼬文档。以下是此答案中示例部分的快照:

// There are 2 ways to use a cursor. First, as a stream:
Thing.
  find({ name: /^hello/ }).
  cursor().
  on('data', function(doc) { console.log(doc); }).
  on('end', function() { console.log('Done!'); });

// Or you can use `.next()` to manually get the next doc in the stream.
// `.next()` returns a promise, so you can use promises or callbacks.
var cursor = Thing.find({ name: /^hello/ }).cursor();
cursor.next(function(error, doc) {
  console.log(doc);
});

// Because `.next()` returns a promise, you can use co
// to easily iterate through all documents without loading them
// all into memory.
co(function*() {
  const cursor = Thing.find({ name: /^hello/ }).cursor();
  for (let doc = yield cursor.next(); doc != null; doc = yield cursor.next()) {
    console.log(doc);
  }
});
Run Code Online (Sandbox Code Playgroud)

考虑到上述情况,您的数据集可能会变得非常大且难以处理。考虑使用 MongoDB 的聚合管道来简化大型数据集的处理可能是一个好主意。如果使用副本集,您甚至可以设置 areadPreference将大型聚合查询定向到辅助节点,确保主节点的性能在很大程度上不受影响。这会将负担从您的服务器转移到不太重要的辅助数据库节点。

如果您的数据集特别大,并且您对相同的文档重复执行相同的计算,您甚至可以考虑将预先计算的聚合结果存储在“基础”文档中,然后将所有未处理的文档应用在该“基础”之上作为“增量” “——也就是说,您可以将计算减少到“自上次保存计算以来的每一次更改”。

最后,还有负载平衡选项。您可以有多个应用程序服务器进行处理,并有一个负载平衡器在它们之间大致均匀地分配请求,以防止任何一台服务器不堪重负。


有很多选项可供您使用,以避免出现系统因所有数据处理而不堪重负的情况。您应该采用的策略在很大程度上取决于您的特定用例。然而,在这种情况下,这似乎是一个假设性问题,因此所提到的其他策略可能不是您需要关心的事情。现在,坚持.next()通话,你应该没问题。