1

我有一个包含 5200 万条记录的用户集合。每个用户文档都有一个评论列表,并且comment_id上面有一个唯一的索引。

{
  _id:123, 
  user_name:"xyz",
  comments:[
    {
      comment_id:123,
      text:"sd"
    },
    {
      comment_id:234,
      text:"sdf"
    }
    ......,
    (63000 elements)
  ]
}

索引的大小totalIndexSizecomment_id104GB。我在 52M 中大约有 100 个文档,其中评论数组中有 63000 个元素。

我的目标是删除旧评论并将评论数组的大小减少 80% 以上。早些时候,当我尝试使用此查询更新文档时

db.user.updateOne({_id:_id},{$set: {"comments":newCommentsArray}},upsert=True)

这里 newCommentsArray 的大小约为 400。此操作执行大约需要 130 秒。

我的问题是:

1)上面的更新查询花费了 130 秒的原因可能是什么。是因为comment_id字段上巨大的唯一索引大小吗?(我相信用新的评论数组更新评论数组将尝试重新排列所有已删除的 63000 个元素的索引并将新元素插入索引中。)

2)我有另一种方法使用$pull,基本上是从评论数组中提取 100 条评论并等待 5 秒,然后执行下一批 100 条评论。你觉得这个解决方案怎么样。

3)如果上述解决方案不好,您能否提出一个将评论数组减少 80% 以上的好方法。

4

1 回答 1

1

你有一个巨大的索引,comment_id因为你有Multikey Index

MongoDB 为数组中的每个元素创建一个索引键。

在您的情况下,_id索引的大小约为 1GB,comment_idavg ~100/per document(获得 ~104GB)

1) 上面的更新查询需要 130 秒的原因可能是什么

Mongodb 使用B-tree 结构存储索引。B树属性:

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)

这意味着,要为评论插入索引,O(log n)在最坏的情况下,MongoDB 需要迭代(每个项目约 25 次迭代)。

2)我有另一种方法使用 $pull 这基本上是从评论数组中提取 100 条评论并等待 5 秒,然后执行下一批 100 条评论。

随着评论被索引,它会很快(记住O (log n)属性)。不需要等待 5 秒,因为从 MongoDB 3.0 开始,它使用多粒度锁定,这意味着只锁定受影响的文档。

此外,您可以使用$push这样的运算符减少:

db.user.update({ },{$push: {comments: {$each: [ ], $slice: -400}}})

这将插入[ ](在本例中为 0 个项目)项目并从末尾开始切片 400 个项目

3)如果上述解决方案不好,您能否提出一个将评论数组减少 80% 以上的好方法。

即使减少注释数组,WiredTiger也不会向操作系统释放不需要的磁盘空间

运行dropIndex

db.user.dropIndex({ "comment_id" : 1 })

警告:由于 v4.2 在操作期间获取指定集合的​​排他锁。集合上的所有后续操作都必须等到 db.collection.dropIndex() 释放锁。

在 v4.2 之前,此命令会在受影响的数据库上获得写锁,并将阻止其他操作,直到它完成。

或运行紧凑

警告: compact阻止当前正在操作的数据库的操作。仅compact在计划的维护期间使用。此外,您必须user使用目标集合上的紧凑权限操作进行身份验证

于 2020-02-22T21:40:32.837 回答