【问题标题】:Implement draft and publication system in MongoDB在 MongoDB 中实现草稿和发布系统
【发布时间】:2018-04-03 07:38:24
【问题描述】:

我有一个 mongodb 数据库,其集合文档大致如下:

// book document
{
    _id: $oid,
    userId: "..."
    name: "name",
    description: "description"
    status: "draft"
    // ...
}

// page document
{
    _id: $oid,
    bookId: "..."
    name: "name",
    description: "description"
    // ...
}

一本书可以有数千页。每本书都有一个status 属性,该属性可以是“草稿”、“已发布”或“已归档”状态之一。

可以从任何其他书籍创建“草稿”书籍,例如具有“草稿”、“已发布”或“存档”状态。

每次创建新草稿时,都会创建一个与原书具有所有相同页面的新书文档。

任何“草稿”书籍及其所有页面都可以独立于原始书籍及其页面进行编辑。这意味着对新草稿书或其任何页面的任何编辑都不会影响原书或其页面。

在设计中,应优先考虑书籍查询性能而不是草稿创建性能,即查询具有所有(分页)页面的书籍是最高性能优先级。

实现上述要求的最佳方法是什么?在此阶段,可以对数据库模式进行任何修改。

这些是我考虑过的选项:

  1. 只要从现有书籍创建新草稿,就可以克隆整本书及其所有页面。这将提供干净的草稿,可以通过简单的find 以最快的方式对其整体进行独立编辑和查询。不利的一面是,page 集合的大小会随着草稿数量的增加而迅速增长,并且克隆书中的大部分页面将与原书中的版本完全相同。此外,草稿创建时间会随着书的页数成比例增长。

  2. parentId 属性添加到book 文档以引用原始book 并重用父级中所有未修改的页面。每次编辑页面时,都会克隆一个新页面,并将 bookId 设置为新草稿的 id。这种方法将重用大部分 page 文档,但查询草稿书会更加复杂,因为我们需要遵循 parentId 链来查找所有修改过的页面和属于所有父书的所有页面(草稿可以是多次创建不同的草稿)。草稿创建会非常快(只需克隆一本书),但查询时间会随着草稿的父级数量成比例增长。此外,当查询一个页面在不同父级中编辑过的草稿时,我们需要过滤掉所有旧页面版本,只保留链中的最新版本。

【问题讨论】:

    标签: mongodb database-design nosql database-versioning


    【解决方案1】:

    您描述的两种方法的行为完全不同。在第一种(完整克隆)情况下,原始页面的更改仅适用于原始书籍。在第二种(分层)情况下,原始页面的更改会自动传播到所有衍生草稿。我不会质疑哪个性能更好,因为这就像比较苹果和橘子。

    考虑第三种选择(更改时复制)- 存储对书中页面的引用:

    // book document
    {
        _id: $oid,
        userId: "..."
        name: "name",
        description: "description"
        status: "draft"
        // ...
        pages: [
            $oid1,
            $oid2,
            $oid3,
            ....
        ]
    }
    

    创建新草稿就像克隆单个书籍文档一样简单。 用页面查询一本书是一种简单的查找聚合。

    最昂贵的操作将是页面更改,如果您需要强大的数据完整性,可能需要2 phase commit

    使用这种方法,您可以选择将更改传播到衍生草稿,或将其保留在本地。如果稍后,您可能需要额外的内务处理步骤来删除孤立页面。

    【讨论】:

    • 是的,在分层情况下,页面的新版本需要隐藏原始书籍中的旧版本。这是相当复杂的,可能需要在应用程序级别完成。我还考虑了您的第三个选项。关于那几个问题 1. 要使用查找聚合,您需要放弃分片集合(尽管我仍然不确定我是否应该普遍关心?) 2. 除了数据完整性问题,您为什么认为更改页面会贵吗?会不会只是创建一个新的页面文档并在页面 id 列表中将旧 id 与新 id 交换?
    • 草稿中的新页面不会替换原始文档中的同一页面 - 这部分适用于您的两种情况。不同之处在于向原始文档添加新页面时。在第一种情况下,它不会添加到草稿中,因为它是在添加新页面之前制作的完整副本。在第二种情况下,新页面也将在草稿中,因为它仍然是对原始文档的引用。
    • 是的,我认为添加到原书中的任何新页面都需要在创建日期之前过滤掉。
    • 关于分片 - 只要书籍保持未分片,就可以对页面进行分片。如果两个集合都需要分片,你可以完全不用聚合。只需进行 2 次查询 - 查找一本书,然后查找页面 {_id: {$in: book.pages.slice(start, end)} }。关于昂贵的 wrights - 是的,由于多个文档的复杂更新,它很昂贵。在页面 id 列表中用新 id 交换旧 id 仍然需要一些努力来确保同一本书的并发更新按预期工作。
    • 谢谢,是的,我想当 MongoDB v4 事务出现时,处理并发更新将被排序。目前,要么是两阶段提交,要么是弱数据完整性。
    猜你喜欢
    • 2014-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 2015-01-10
    • 1970-01-01
    • 2013-04-02
    • 2018-03-02
    相关资源
    最近更新 更多