一对一嵌套关系模型
在本页面
这章节使用嵌套文档的模型描述了具有一对一关系的数据实体。把有关联的数据嵌套在单个文档中,可以减少读操作的次数。通常来说,如果你按嵌套文档模式来设计你的数据结构,那么在一次读取操作里,你的应用程序会接收文档所有的信息。
考虑下面 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"
}
如果以引用的方式频繁读取name
和 address
的数据,那么你的应用程序需要查询多次才能获取信息。更好的方式应该把 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
集合包含了应用程序不需要的几个的字段,比如像 fullplot
和 rating
的值。并不是要把电影的所有的数据都存储在单个集合里,你可以把单个集合分离成两个集合:
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/
译者:朱俊豪
最后更新于