文档数据库:冗余数据、引用等(特别是MongoDB)

37

似乎我经常遇到需要将数据拆分为两个文档的情况。 假设这是一家连锁店,您要保存每个顾客访问的哪些商店。 商店和顾客需要独立的数据因为它们与许多其他事物进行交互,但我们确实需要将它们联系起来。

因此,简单的答案是在商店文档中存储用户ID,在用户文档中存储商店ID。 但通常情况下,您想要访问1-2个其他的数据以便于显示,因为ID并不有用。 比如说顾客姓名或商店名称。

  1. 你通常会存储整个文档的副本吗?还是只存储所需的数据片段?也许这取决于文档的大小以及你需要多少其中的内容。
  2. 如何处理重复数据的问题?当数据更改时是否需要寻找数据?在加载时更新数据间隔时间?只有在可以承受过期数据时才重复?

希望您能提供意见和/或任何有关这些主题的“最佳实践”或至少合理推论的链接。

3个回答

37
基本上有两种情况: 新鲜陈旧

新鲜数据

存储重复的数据很容易。维护重复的数据是困难的部分。因此,最简单的方法就是避免维护,只需一开始就不存储任何重复数据。如果你需要 新鲜数据,那么只需存储引用,并在需要检索信息时查询集合。
在这种情况下,由于额外的查询而产生一些开销。另一种选择是跟踪所有重复数据的位置,并在每次更新时更新所有实例。这也涉及一些开销,尤其是在像你提到的 N-to-M 关系中。所以无论哪种方式,如果需要新鲜数据,都会有一些开销。你不能两全其美。

陈旧数据

如果你能够接受陈旧的数据,事情就变得容易得多了。为了避免查询开销,可以存储重复数据。为了避免维护重复数据,你不会主动存储重复数据。
在这种情况下,你也只需存储文档之间的引用。然后使用定期的 Map-Reduce 作业生成重复数据。然后你就可以查询单个 Map-Reduce 结果,而不是分开的集合。这样你就避免了查询开销,也不必寻找数据变更。

摘要

只存储其他文档的引用。如果你能接受陈旧的数据,使用定期的 Map-Reduce 作业来 生成 重复数据。避免 维护 重复数据;它很复杂且容易出错。

1
好的,总体来说我理解了。唯一让我不太清楚的是你描述的map-reduce结果情况,它似乎假定所有数据都需要相同的新鲜度。在这个例子中,用户数据必须是新鲜的,但用户的商店名称数据可以过期。因此,我不想从定期的map-reduce中读取用户数据和商店数据,因为用户数据不能过期。那么这是否完全迫使我进入“新鲜”场景呢? - Jim
1
@Jim:如果数据的一部分,比如已访问商店的名称,可以过时,你可以使用Gates VP的解决方案。只要记得在更新Stores中的商店名称时,也要更新Customer文档。 - Niels van der Rest
1
@NielsvanderRest,你能详细解释一下MapReduce吗? - babak faghihian
我不熟悉“陈旧”和“新鲜”的数据术语。这些术语是什么意思? - Hatshepsut

16

这里的答案实际上取决于你需要多及时的数据。

@Niels 在这里有一个很好的总结,但我认为值得一提的是你可以“作弊”。

假设您想要显示用户使用的商店。 显然问题在于您无法将商店“嵌入”到用户中,因为商店本身太重要了。 但是您可以在用户中嵌入某些商店数据。

只需使用您想要用于显示的内容,例如“商店名称”。 因此,您的用户对象将如下所示:

{
  _id : MongoID(),
  name : "Testy Tester",
  stores : [ 
             { _id : MongoID(), "name" : 'Safeway' },
             { _id : MongoID(), "name" : 'Walmart' },
             { _id : MongoID(), "name" : 'Best Buy' }
            ]
}

通过这种方式,您可以显示典型的“网格”视图,但需要链接来获取有关商店的更多数据。


5
当数据定期生成或基于现有数据时,这是一种好的方法。如果您手动插入额外的数据,您也必须手动更新它。当然,对于不太可能更改的事物(如商店名称),这并不是问题。+1 - Niels van der Rest

1

回答你的直接问题:

  1. 不要有重复。
  2. 不要有重复。

;)

你唯一可以拥有的重复值是“简单”的值,比如权重(可能相同,但单独存储既不高效也不节省空间),以及引用另一个对象的id(它们是重复的值,但比替代的重复对象数据更小更易处理)。

现在,回答你的情境:你需要的是多对多关系。通常的解决方案是创建第三个“通过”或“桥接”表/集合,可能称为StoreUsers:

StoreUsers
----------
storeuser_id
store_id
user_id

您需要为每个商店和用户之间的链接添加一条记录,无论是针对不同的商店、不同的用户还是一个商店中的一堆用户。您可以独立查找此信息,无论是针对商店还是用户。 MongoDB 也提倡这种方法;它并非只适用于 RDBMS。


5
等一下!这和RDBMS有什么区别呢? - Vaibhav
12
在与Mongo一起处理一个大型项目时,如果考虑这个答案是正确的方法,我会感到非常失望。一旦使用引用,Mongo比任何关系型数据库都要慢得多。我正在尝试通过检查最佳的数据复制方式来解决这个问题,但我看到的所有建议都是像在关系型数据库中一样操作...那么MongoDB的用途是什么? - Laurent
1
这是一个非关系型数据库,它实现了我们的数据倾向于关系型 :) - aelmosalamy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接