更新Mongodb中的嵌入式文档:性能问题?

时间:2016-10-20 14:33:21

标签: mongodb mongodb-query

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

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

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

  1. 搜索所有图书文档,并使用此作者挑选出数千篇文档
  2. 更新作者在这些图书文档中的电子邮件字段
  3. 这些操作效率似乎不高。但是这种类型的更新无处不在,我相信开发人员已经考虑过这一点。那么,我哪里弄错了?

2 个答案:

答案 0 :(得分:2)

您当前的嵌入式架构设计有其优点,其中之一是数据局部性。由于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"
}

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

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

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

一般的经验法则是,如果您的应用程序的查询模式是众所周知的,并且数据往往只能以一种方式访问​​,那么嵌入式方法效果很好。如果您的应用程序以多种方式查询数据,或者您无法预测数据查询模式,则更为规范化的文档引用模型将适用于此类情况。

价:

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

答案 1 :(得分:0)

我认为你基本上有错误的架构设计。 MongoDB允许您以层次结构方式构建数据,但这并不是低效构建数据的借口。如果您可能需要定期更新整个馆藏中的数千个文档,那么值得一提的是您是否拥有正确的架构设计。

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