遍历MongoDB游标速度缓慢

5
我需要遍历一个包含大约2百万个文档的MongoDb集合。因此,我使用了游标特性和eachAsync函数。然而,我发现它非常慢(需要超过40分钟)。我尝试了不同的batchSizes,最高达到5000(这只需要对MongoDB进行400次查询)。
应用程序不需要太多的CPU(0.2%-1%),也不需要太多的RAM或IOPs。因此,显然我的代码可以优化以加快这个过程。 代码:
  const playerProfileCursor = PlayerProfile.find({}, { tag: 1 }).cursor({ batchSize: 5000 })
  const p2 = new Promise<Array<string>>((resolve, reject) => {
    const playerTags:Array<string> = []
    playerProfileCursor.eachAsync((playerProfile) => {
      playerTags.push(playerProfile.tag)
    }).then(() => {
      resolve(playerTags)
    }).catch((err) => {
      reject(err)
    })
  })

当我在eachAsync函数体内设置断点时,它会立即被触发。所以没有任何卡顿,只是速度非常慢。有没有办法加速这个过程?


你尝试过对你的应用进行性能分析吗?例如使用 https://github.com/v8/v8/wiki/V8-Profiler ? - Alex Blex
@Styx 是的,我需要从两个不同的集合中获取所有的 tag,并找到这些标签数组的联合。 - kentor
所以你需要从每个集合中获取唯一的标签,对吗? - Styx
1
嗯,对我来说这似乎是一个“XY问题”。有一种更好的方法可以获取文档特定字段的唯一值,而不必遍历所有文档。 - Styx
这段代码是为了在stackoverflow上简化,但我还需要遍历整个集合来完成其他任务。从200万个文档中收集一个数字不应该需要30分钟左右的时间。 - kentor
显示剩余5条评论
1个回答

7

该功能是在版本4.12中添加的(目前最新版本),但并没有真正记录。

eachAsync 默认情况下具有1个并发度,但可以在参数“parallel”中更改。(如此看来)

因此,您的代码可能看起来像这样:

const playerProfileCursor = PlayerProfile.find({}, { tag: 1 }).cursor({ batchSize: 5000 })
const p2 = new Promise<Array<string>>((resolve, reject) => {
const playerTags:Array<string> = []
playerProfileCursor.eachAsync((playerProfile) => {
  playerTags.push(playerProfile.tag)
}, { parallel: 50 }).then(() => {
  resolve(playerTags)
}).catch((err) => {
  reject(err)
})
})

那真的起了作用,我猜那是被很好地隐藏了:D。现在速度快多了,CPU使用率也更高了(正如预期的那样)。 - kentor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接