【问题标题】:MongoDb Node.js slow concurrent queriesMongoDb Node.js 慢并发查询
【发布时间】:2019-10-17 18:06:52
【问题描述】:

当我使用 Node.js 运行并发 MongoDb 查询时,第二个查询总是需要大约 2 秒才能返回。使用explain()executionTimeMillis 总是返回 0ms,这是绝对正常的,因为我的测试集合只有 2 个条目。这是我的简化测试用例:

'use strict'

const { MongoClient } = require('mongodb')

const main = async () => {
    const client = new MongoClient('mongodb://admin:123456@localhost:27017/', {
        useNewUrlParser: true,
        useUnifiedTopology: true,
    })

    await client.connect()

    const db = client.db('test')
    const numbers = db.collection('numbers')

    const promises = []

    console.time()

    for (let i = 0; i < 3; i++) {
        promises.push(numbers.find({ number: i }).explain())
    }

    for (const promise of promises) {
        console.log(await promise)
        console.timeLog()
    }

    console.timeEnd()

    await client.close()
}

main()

输出:

{
  queryPlanner: {
    plannerVersion: 1,
    namespace: 'test.numbers',
    indexFilterSet: false,
    parsedQuery: { number: [Object] },
    winningPlan: { stage: 'FETCH', inputStage: [Object] },
    rejectedPlans: []
  },
  executionStats: {
    executionSuccess: true,
    nReturned: 1,
    executionTimeMillis: 0,
    totalKeysExamined: 1,
    totalDocsExamined: 1,
    executionStages: {
      stage: 'FETCH',
      nReturned: 1,
      executionTimeMillisEstimate: 0,
      works: 2,
      advanced: 1,
      needTime: 0,
      needYield: 0,
      saveState: 0,
      restoreState: 0,
      isEOF: 1,
      invalidates: 0,
      docsExamined: 1,
      alreadyHasObj: 0,
      inputStage: [Object]
    },
    allPlansExecution: []
  },
  serverInfo: {
    host: 'DESKTOP-C7CAL9N',
    port: 27017,
    version: '4.0.10',
    gitVersion: 'c389e7f69f637f7a1ac3cc9fae843b635f20b766'
  },
  ok: 1
}
default: 32.252ms
{
  queryPlanner: {
    plannerVersion: 1,
    namespace: 'test.numbers',
    indexFilterSet: false,
    parsedQuery: { number: [Object] },
    winningPlan: { stage: 'FETCH', inputStage: [Object] },
    rejectedPlans: []
  },
  executionStats: {
    executionSuccess: true,
    nReturned: 1,
    executionTimeMillis: 0,
    totalKeysExamined: 1,
    totalDocsExamined: 1,
    executionStages: {
      stage: 'FETCH',
      nReturned: 1,
      executionTimeMillisEstimate: 0,
      works: 2,
      advanced: 1,
      needTime: 0,
      needYield: 0,
      saveState: 0,
      restoreState: 0,
      isEOF: 1,
      invalidates: 0,
      docsExamined: 1,
      alreadyHasObj: 0,
      inputStage: [Object]
    },
    allPlansExecution: []
  },
  serverInfo: {
    host: 'DESKTOP-C7CAL9N',
    port: 27017,
    version: '4.0.10',
    gitVersion: 'c389e7f69f637f7a1ac3cc9fae843b635f20b766'
  },
  ok: 1
}
default: 2042.929ms
{
  queryPlanner: {
    plannerVersion: 1,
    namespace: 'test.numbers',
    indexFilterSet: false,
    parsedQuery: { number: [Object] },
    winningPlan: { stage: 'FETCH', inputStage: [Object] },
    rejectedPlans: []
  },
  executionStats: {
    executionSuccess: true,
    nReturned: 0,
    executionTimeMillis: 0,
    totalKeysExamined: 0,
    totalDocsExamined: 0,
    executionStages: {
      stage: 'FETCH',
      nReturned: 0,
      executionTimeMillisEstimate: 0,
      works: 1,
      advanced: 0,
      needTime: 0,
      needYield: 0,
      saveState: 0,
      restoreState: 0,
      isEOF: 1,
      invalidates: 0,
      docsExamined: 0,
      alreadyHasObj: 0,
      inputStage: [Object]
    },
    allPlansExecution: []
  },
  serverInfo: {
    host: 'DESKTOP-C7CAL9N',
    port: 27017,
    version: '4.0.10',
    gitVersion: 'c389e7f69f637f7a1ac3cc9fae843b635f20b766'
  },
  ok: 1
}
default: 2062.851ms
default: 2063.513ms

如果我依次运行查询,则每个查询只需几毫秒即可返回。那为什么是2秒的响应时间呢?

编辑:

在第一个 for 循环中,我创建/运行了“并发”查询 promises.push(numbers.find({ number: i }).explain())。在第二个 for 循环中,我等待一个又一个的 Promise 解决,但这并不意味着一个 Promise 必须等到前一个解决后才能开始其工作。

为避免误解,我对我的代码做了一些改动,将两个 for 循环替换为:

    for (let i = 0; i < 3; i++) {
        promises.push(
            numbers
                .find({ number: i })
                .explain()
                .then(result => {
                    // console.log(result)
                    console.log('query index:', i)
                    console.timeLog()
                })
        )
    }

    await Promise.all(promises)

输出:

query index: 0
default: 22.040ms
query index: 2
default: 2032.921ms
query index: 1
default: 2034.682ms
default: 2035.260ms

编辑 2:

为了进一步说明,我使用标签来表示计时器。

    for (let i = 0; i < 3; i++) {
        console.time(`query index: ${ i }`)

        promises.push(
            numbers
                .find({ number: i })
                .explain()
                .then(result => {
                    // console.log(result)
                    console.timeEnd(`query index: ${ i }`)
                })
        )
    }

    await Promise.all(promises)

输出:

query index: 0: 12.692ms
query index: 1: 2015.143ms
query index: 2: 2015.310ms

【问题讨论】:

  • 您的查询以瀑布形式执行,第一个查询需要 1 秒,然后第二个查询需要 2 秒(从开始检查点开始)...
  • @hoangdv 请看我的编辑。第一个查询只需 20 毫秒即可完成。预计第二个和第三个在 ~20ms 或最多 ~50ms 内完成。
  • 让我们使用console.timelabel 参数来明确情况。标签是循环的索引,在将查询推送到promise数组之前需要调用console.time(i)console.endTime(i)需要在查询的.then函数中设置。
  • 将 MongoClient 的 poolSize 设置为 1,然后重试。
  • 真的 :ooo 谢谢。我想我现在明白了一些事情。如果我们在之前的查询处于活动状态时运行新查询,mongo 客户端会创建一个新连接(只要当前活动连接的数量&lt; poolSize),这将在我的机器上花费 2 秒。在此之前,我尝试将poolSize 设置为更高的数字,甚至没有考虑poolSize=1! :d 谢谢。请添加上述评论作为答案,以便我接受。

标签: node.js mongodb


【解决方案1】:

将 MongoClient 的 poolSize 设置为 1。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-30
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    • 2019-06-25
    • 2020-03-21
    • 1970-01-01
    相关资源
    最近更新 更多