mongodb - 如何在没有竞争条件的情况下使用 mongodb 实现有序数组?

标签 mongodb optimization

我是 mongodb 的新手,也许这是一个微不足道的问题。我有两个 mongodb 集合:userpost。用户可以创建和关注多个帖子,帖子按最后修改日期排序。可能有大量用户关注特定帖子,因此我不想在每个帖子文档中保留关注者列表。另一方面,一个用户可能不会关注超过几千个帖子,所以我决定在每个用户文档中保留关注帖子的 objectid 列表。

为了能够快速列出给定用户最近修改的 50 个帖子,我选择将 last_updated_at 字段与帖子 objectid 一起保留。

post 文档相当基本:

{
    "_id" : ObjectId("5163deebe4d809d55d27e847"),
    "title" : "All about music"
    "comments": [...]
    ...
}

user 文档如下所示:

{
  "_id": ObjectId("5163deebe4d809d55d27e846"),
  "posts": [{
    "post": ObjectId("5163deebe4d809d55d27e847"),
    "last_updated_at": ISODate("2013-04-09T11:27:07.184Z")
  }, {
    "post": ObjectId("5163deebe4d809d55d27e847"),
    "last_updated_at": ISODate("2013-04-09T11:27:07.187Z")
  }]
  ...
}

当用户创建或关注帖子时,我可以简单地 $push 帖子的 ObjectIdlast_updated_atposts 用户文档中的列表。当帖子被修改时(例如,当评论被添加到帖子中时),我会在所有关注者的用户文档中更新该帖子的 last_updated_at 字段。这很重,但我不知道如何避免它。

当我想获取用户最近更新的 50 个帖子的列表时,不幸的是,我需要获取关注的帖子的整个列表,然后在内存中按 last_updated_at 排序,然后只保留前 50 个帖子。

所以我尝试更改实现以在修改帖子时重新排序列表:我将其 $push 到列表末尾,然后 $pull 从无论它在哪里。由于这是一个两步程序,因此存在竞争条件,我可能会在列表中获得两次相同的帖子。有没有更好的方法来维护mongodb中的排序数组?

最佳答案

数据模型调整

由于您可能经常更新给定用户的最新帖子,因此您可能希望避免不必要地重写数据以维护排序数组的开销。

要考虑的更好方法是展平数据模型并使用单独的集合而不是有序数组:

  • 使用更新后的帖子流创建一个单独的集合:(userID, postID, lastUpdated)
  • 当帖子更新时,您可以做一个简单的 update()使用 multi:trueupsert:true 选项和 $set 将 last_updated_at 设置为新值。
  • 要检索给定用户 ID 的最近 50 条更新帖子,您可以使用排序和限制选项执行普通 find()
  • 要自动清理“旧”文档,您甚至可以设置 TTL expiry对于此集合,因此更新会在一定天数后从事件流中删除

在 MongoDB 2.4 中推送到固定大小和排序的数组

如果您确实想维护有序数组,MongoDB 2.4 添加了两个与此用例相关的有用功能:

  • 能够推送到固定大小的数组
  • 能够推送到按嵌入式文档字段排序的数组

因此,您可以实现推送到按上次更新日期降序排列的 50 个固定大小的数组的结果:

db.user.update(
    // Criteria
    { _id: ObjectId("5163deebe4d809d55d27e846") },

    // Update
    { $push: {
        posts: {
            // Push one or more updates onto the posts array
            $each: [
                {
                    "post": ObjectId("5163deebe4d809d55d27e847"),
                    "last_updated_at": ISODate()
                }
            ],

            // Slice to max of 50 items
            $slice:-50,

            // Sorted by last_updated_at desc
            $sort: {'last_updated_at': -1}
        }
    }}
)

$push 将按排序顺序更新列表,$slice 将列表修剪为前 50 个项目。由于帖子不是唯一的,您仍然需要首先从列表中 $pull 原件,例如:

db.user.update(
    // Criteria
    { _id: ObjectId("5163deebe4d809d55d27e846") },

    // Update
    { 
        $pull: {
            posts: { post: ObjectId("5163deebe4d809d55d27e847") }
        }
    }
)

这种方法的一个好处是数组操作是在服务器上完成的,但是与在应用程序中对数组进行排序一样,您可能仍然需要更新文档而不是需要。

关于mongodb - 如何在没有竞争条件的情况下使用 mongodb 实现有序数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15899368/

相关文章:

PHP 身份验证/ session 处理 : Ground Up or Framework/Package?

javascript - Mongoose/Mongo 和保存

arrays - 更新 MongoDB 中的子文档数组

javascript - Node.js:如何处理新 URL 解析器弃用警告?

python - 需要一个测试用例,其中给定的最小硬币数量代码在 python 中失败了吗?

MongoDB 聚合排序不起作用

mysql - 需要调整哪些设置来改进包含大量列和少量联接的 SELECT?

optimization - 为一种可以预见和多个文件的语言编写一个编译器?

mysql - MySql 索引表中固定的 preprendet 字符串是否会导致性能问题?

sql - T-SQL ISNULL() 优化