一对一嵌套关系模型

在本页面

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

考虑下面 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/MV5BMjEyNDk5MDYzOV5BMl5BanBnXkFtZTgwNjIxMTEwMzE@._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/MV5BMjEyNDk5MDYzOV5BMl5BanBnXkFtZTgwNjIxMTEwMzE@._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/

译者:朱俊豪

最后更新于