【问题标题】:Update embedded document in Mongodb: Performance issue?更新 Mongodb 中的嵌入文档:性能问题?
【发布时间】:2016-10-21 00:24:31
【问题描述】:

我是 Mongodb 的新手,听说 Mongodb 适合大量的读写操作。 嵌入式文档是实现它的功能之一。但我不确定这是否也是性能问题的原因。 图书文档示例:

{
    "_id": 1,
    "Authors": [
        {
            "Email": "email",
            "Name": "name"
        }
    ],
    "Title": "title",
    ...
}

如果一位作者有数千本书,并且他的电子邮件需要更新,我需要编写一些查询

  1. 搜索所有书籍文档,挑选出与该作者有关的数千篇
  2. 在这些图书文档中更新作者的电子邮件字段

这些操作似乎效率不高。但是这种类型的更新无处不在,相信开发者已经考虑到了这一点。那么,我哪里弄错了?

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    您当前的嵌入式架构设计有其优点,其中之一是数据局部性。由于 MongoDB 将数据连续存储在磁盘上,因此将所需的所有数据放在一个文档中可确保旋转磁盘花费更少的时间来寻找磁盘上的特定位置。

    如果您的应用程序经常访问books 信息以及Authors 数据,那么您几乎肯定会希望采用嵌入式路由。嵌入文档的另一个优点是写入数据的原子性和隔离性。

    为了说明这一点,假设您希望一位作者的所有书籍都更新他的电子邮件字段,这可以通过一个(原子)操作来完成,这不是 MongoDB 的性能问题:

    db.books.updateMany(
        { "Authors.name": "foo" },
        {
            "$set": { "Authors.$.email": "new@email.com" }
        }
    );
    

    或使用早期的 MongoDB 版本:

    db.books.update(
        { "Authors.name": "foo" },
        {
            "$set": { "Authors.$.email": "new@email.com" }
        },
        { "multi": true }
    )
    

    在上面,您使用positional $ operator,它通过识别数组中要更新的元素而不显式指定数组中元素的位置来促进对包含嵌入文档的数组的更新。将它与$ 运算符上的dot notation 一起使用。

    有关 MongoDB 中数据建模的更多详细信息,请阅读文档Data Modeling Introduction,尤其是Model One-to-Many Relationships with Embedded Documents


    您可以考虑的另一个设计选项是引用您遵循规范化架构的文档。例如:

    // db.books schema
    {
        "_id": 3
        "authors": [1, 2, 3] // <-- array of references to the author collection
        "title": "foo"
    }
    
    // db.authors schema
    /*
    1
    */
    {
        "_id": 1,    
        "name": "foo",
        "surname": "bar",
        "address": "xxx",
        "email": "foo@mail.com"
    }
    /*
    2
    */
    {
        "_id": 2,    
        "name": "abc",
        "surname": "def",
        "address": "xyz",
        "email": "abc@mail.com"
    }
    /*
    3
    */
    {
        "_id": 3,    
        "name": "alice",
        "surname": "bob",
        "address": "xyz",
        "email": "alice@mail.com"
    }
    

    当您具有非常不可预测的数量的一对多关系时,上述使用文档引用方法的规范化模式也具有优势。如果每个图书实体有数百或数千个作者文档,则嵌入在空间限制方面会遇到很多挫折,因为文档越大,它使用的 RAM 越多,而 MongoDB 文档的硬大小限制为 16MB。

    对于查询规范化架构,您可以考虑使用聚合框架的 $lookup 运算符,该运算符对同一数据库中的 authors 集合执行左外连接,以过滤来自 @ 的文档987654337@收集处理。


    因此,我相信您当前的架构比创建 authors 的单独集合更好,因为单独的集合需要更多的工作,即查找一本书 + 它的作者是两个查询并且需要额外的工作,而上述架构嵌入文档简单快捷(单次搜索)。插入和更新没有太大区别。因此,如果您需要选择单个文档、需要对查询进行更多控制或拥有大量文档,则单独的集合是很好的选择。当您想要整个文档时,嵌入文档也很好,该文档具有 $slice 的嵌入 authors,或者根本没有 authors

    一般的经验法则是,如果您的应用程序的查询模式广为人知,并且往往只能以一种方式访问​​数据,那么嵌入式方法就可以很好地工作。如果您的应用程序以多种方式查询数据,或者您无法预测数据查询模式,则更规范化的文档引用模型将适合这种情况。

    参考:

    MongoDB Applied Design Patterns: Practical Use Cases with the Leading NoSQL Database By Rick Copeland

    【讨论】:

      【解决方案2】:

      我认为您基本上有错误的架构设计。 MongoDB 允许您按层次结构构建数据,但这并不是构建数据效率低下的借口。如果您可能需要定期更新整个集合中的数千个文档,那么值得询问您是否有正确的架构设计。

      有很多关于架构设计的文章,以及与关系数据库结构的比较。例如: http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-15
        • 2011-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多