一对一嵌套关系模型

在本页面

概述¶

这章节使用嵌套文档的模型描述了具有一对一关系的数据实体。把有关联的数据嵌套在单个文档中,可以减少读操作的次数。通常来说,如果你按嵌套文档模式来设计你的数据结构,那么在一次读取操作里,你的应用程序会接收文档所有的信息。

考虑下面 patron 和 address 映射关系的示例。这个示例说明嵌套文档优于引用:你需要在一个数据实体的内部查看另一个数据实体的信息。数据实体 patron 和 数据实体address 的关系是一对一的,一个 address 数据实体属于一个 patron 数据实体。

在标准化数据模型里,address 文档包含了 patron文档的引用。

// patron document
// patron 文档
{
_id: "joe",
name: "Joe Bookreader"
}
// address document
// address 文档
{
patron_id: "joe", // reference to patron document // patron文档的引用
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}

如果以引用的方式频繁读取nameaddress 的数据,那么你的应用程序需要查询多次才能获取信息。更好的方式应该把 address 实体嵌套到 patron 实体内,像下面这个示例。

{
_id: "joe",
name: "Joe Bookreader",
address: {
street: "123 Fake Street",
city: "Faketon",
state: "MA",
zip: "12345"
}
}

在嵌套文档模型里,你的应用程序查询一次就能获取 patron 的完整信息。

嵌套文档模型有一个潜在问题是:当文档包含了应用程序不需要的字段时,它会导致文档过大。这些冗余的数据会造成服务器的额外开销从而降低读的性能。相反,你可以把频繁被访问的数据子集放在单独的数据库中,以子集模式去方式去获取。

考虑一个应用会呈现电影的信息。数据库包含了一个 movie 集合,movie 的模式如下。

{
"_id": 1,
"title": "The Arrival of a Train",
"year": 1896,
"runtime": 1,
"released": ISODate("01-25-1896"),
"poster": "http://ia.media-imdb.com/images/M/[email protected]_V1_SX300.jpg",
"plot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, ...",
"fullplot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, the line dissolves. The doors of the railway-cars open, and people on the platform help passengers to get off.",
"lastupdated": ISODate("2015-08-15T10:06:53"),
"type": "movie",
"directors": [ "Auguste Lumière", "Louis Lumière" ],
"imdb": {
"rating": 7.3,
"votes": 5043,
"id": 12
},
"countries": [ "France" ],
"genres": [ "Documentary", "Short" ],
"tomatoes": {
"viewer": {
"rating": 3.7,
"numReviews": 59
},
"lastUpdated": ISODate("2020-01-09T00:02:53")
}
}

目前,在展示一部电影的简介时,movie 集合包含了应用程序不需要的几个的字段,比如像 fullplotrating 的值。并不是要把电影的所有的数据都存储在单个集合里,你可以把单个集合分离成两个集合:

  • movie 集合包含了一部电影的基本信息。应用程序会默认加载这个文档数据。

    // movie collection
    // movie 集合
    {
    "_id": 1,
    "title": "The Arrival of a Train",
    "year": 1896,
    "runtime": 1,
    "released": ISODate("1896-01-25"),
    "type": "movie",
    "directors": [ "Auguste Lumière", "Louis Lumière" ],
    "countries": [ "France" ],
    "genres": [ "Documentary", "Short" ],
    }
  • movie_details 集合包含了每部电影额外的,较少访问的数据。

    // movie_details collection
    // movie_details 集合
    {
    "_id": 156,
    "movie_id": 1, // reference to the movie collection
    "poster": "http://ia.media-imdb.com/images/M/[email protected]_V1_SX300.jpg",
    "plot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, ...",
    "fullplot": "A group of people are standing in a straight line along the platform of a railway station, waiting for a train, which is seen coming at some distance. When the train stops at the platform, the line dissolves. The doors of the railway-cars open, and people on the platform help passengers to get off.",
    "lastupdated": ISODate("2015-08-15T10:06:53"),
    "imdb": {
    "rating": 7.3,
    "votes": 5043,
    "id": 12
    },
    "tomatoes": {
    "viewer": {
    "rating": 3.7,
    "numReviews": 59
    },
    "lastUpdated": ISODate("2020-01-29T00:02:53")
    }
    }

这种方法提高了读取性能,因为它要求应用程序读取更少的数据来满足最常见的需求。如果需要那些较少被访问的数据,应用程序会调用其他的数据库。

提示:

当考虑在哪里分离你的数据时,在文档中频繁被访问的部分应该被分离出来,因为它会被应用程序首先加载。

另请参阅

了解如何使用子集模式到一对多关系集合模型中,参阅 Model One-to-Many Relationships with Embedded Documents

子集模式的权衡

使用包含频繁被访问数据的小文档会减少工作集的大小。这些较小的文档集会提升了读取性能,并为应用程序提供更多内存可用。

然而,理解你的应用程序及其加载数据的方式是重要的。如果分离数据不当,你的应用程序会经常需要多次访问数据库和依赖 JOIN 操作才能获取应用程序需要的全部数据。

另外,你的数据被分离成很多个集合,会增加数据库的维护成本。因为它会使数据存储和数据查询变得困难。

原文链接:https://docs.mongodb.com/v4.2/tutorial/model-embedded-one-to-one-relationships-between-documents/

译者:朱俊豪