【问题标题】:Slow array $push mongoDB query慢速数组 $push mongoDB 查询
【发布时间】:2020-05-20 17:43:06
【问题描述】:

我有一个非常简单的集合,除了 _id 之外没有任何索引。 每个文档都包含一个数组字段mies。 当前的集合大小约为 1 亿,我可以在分析器中看到以下异常:

{
  "op": "update",
  "ns": "DB.links",
  "command": {
    "q": {
      "_id": "f1b54f37-7f92-4e75-9ec6-5329349ce792_eb370c8a-6f33-4989-aa59-a26e1c9df46c"
    },
    "u": {
      "$push": {
        "mies": {
          "$each": [
            {
              "$oid": "5e39d07bec34b8000e7f86b7"
            }
          ]
        }
      }
    },
    "multi": true,
    "upsert": true
  },
  "keysExamined": 0,
  "docsExamined": 0,
  "nMatched": 0,
  "nModified": 0,
  "upsert": true,
  "keysInserted": 1,
  "numYield": 0,
  "locks": {
    "Global": {
      "acquireCount": {
        "r": 2,
        "w": 2
      }
    },
    "Database": {
      "acquireCount": {
        "w": 2
      },
      "acquireWaitCount": {
        "w": 1
      },
      "timeAcquiringMicros": {
        "w": 19486143
      }
    },
    "Collection": {
      "acquireCount": {
        "w": 1
      }
    },
    "oplog": {
      "acquireCount": {
        "w": 1
      }
    }
  },
  "millis": 19490,
  "planSummary": "IDHACK",
  "execStats": {
    "stage": "UPDATE",
    "nReturned": 0,
    "executionTimeMillisEstimate": 0,
    "works": 2,
    "advanced": 0,
    "needTime": 1,
    "needYield": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "nMatched": 0,
    "nWouldModify": 0,
    "nInvalidateSkips": 0,
    "wouldInsert": true,
    "fastmodinsert": false,
    "inputStage": {
      "stage": "IDHACK",
      "nReturned": 0,
      "executionTimeMillisEstimate": 0,
      "works": 1,
      "advanced": 0,
      "needTime": 0,
      "needYield": 0,
      "saveState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "keysExamined": 0,
      "docsExamined": 0
    }
  }
}

如您所见,一个带有单个 $push 到数组的简单 upsert 需要 19 秒。 我相信大部分时间都花在了这里:

      "timeAcquiringMicros": {
        "w": 19486143
      }

我应该检查什么?如何提高性能?

【问题讨论】:

  • 因为你只查询_id 这是一个默认的索引字段,所以查询是安静的,但关心的是1亿个文档,也许你为什么不考虑分片或如果大量读取不需要,以某种方式归档旧记录?
  • 可以,但是由于业务逻辑,数据要积累一段时间。我可以看到热集合的索引大小超过了 MongoDB 的缓存大小。这就是锁慢的原因吗?
  • 我会说至少将该集合与其他集合分开并在可能的情况下将其单独维护在不同的集群中(如果您不想进行分片并希望所有文档都在同一个集群上) - 那样您可以拥有足够好的集群大小(RAM + 磁盘)来满足该集合以及高 I/O... 检查此文档 :: docs.mongodb.com/manual/tutorial/ensure-indexes-fit-ram

标签: mongodb query-optimization


【解决方案1】:

虽然这并不能直接为您的问题提供解决方案,但我没有看到任何其他人提到此问题的潜在原因。您操作缓慢的原因之一,是由于how mongo handles this operation;具体来说,关于 mongo 的复制设计。 如果你不关心解释,直接跳到底部。

根据上面的链接:

修改主节点数据的每个操作都存储在一个特殊的上限集合中,称为 oplog(操作日志的缩写)。 oplog 异步同步到所有从节点,这些从节点重播操作,最终与主节点保持一致。

要使 oplog 工作,它需要是 idempotent。鉴于$push 不是,mongo 将此操作转换为$set - 替换整个数组。

最重要的是,每当您将某些内容推送到数组时,就复制而言,整个数组都会被替换。

【讨论】:

    【解决方案2】:

    你不能

    MongoDB 使用B-tree 算法来索引操作,例如:插入、搜索、删除。

    Algorithm   Average     Worst case
    Space       O(n)        O(n)
    Search      O(log n)    O(log n)
    Insert      O(log n)    O(log n)
    Delete      O(log n)    O(log n)
    

    MongoDB 需要向下钻取有序索引以查找值。由于_id 是唯一的默认索引,因此MongoDB 需要27 迭代才能在最坏的情况下找到文档(134.217.728 [~19sec] 为 227,228 sup> 对于268.435.456 [~21sec] 等...)

    您可以通过创建compound index 来改进search 操作,但会惩罚insert,因为MongoDB 需要更新_id 索引+cound index

    【讨论】:

    • 你说的是哪个复合索引?以及搜索与 19 秒锁定操作有什么关系?
    • 假设您只有 100 个文档,而 MongoDB 每次迭代需要 1 秒。如果您没有索引,MonogDB 会从 1、2、3、... 100(100 秒)开始扫描(也称为 O(n))。但是使用 B-tree 你只需要 6 次尝试(6 秒)(讨论 here)。复合索引讨论here
    • 我知道 BTree 是什么,以及复合索引。不幸的是,它与问题无关
    • @silent-box 我已经回答了你的主要问题:How can I improve the performance? 并解释了为什么它不可能
    猜你喜欢
    • 2019-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-26
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多