0

我想实现一个用户关注系统。一个用户可以关注其他用户。我正在考虑两种方法。一种是模式中存在followersand followeesUser它们都是 user 的数组_id。另一个是只存在followers于模式中。每当我想找到一个用户的关注者时,我都必须搜索所有用户的followers数组,即db.user.find( { followers: "_id" } );. 两种方法的优缺点是什么?谢谢。

4

2 回答 2

1

您在这里考虑的是经典的“多对多”关系。与 RDBMS 不同,在 RDBMS 中,此模式只有一个“正确”范式,在 MongoDB 中,正确的模式设计取决于您使用数据的方式,以及您在此处未提及的其他几个因素.

请注意,对于本次讨论,我假设“跟随”关系不是对称的——也就是说,A 可以跟随 B 而 B 不必跟随 A。

1)在 MongoDB 中有两种基本的方法来建模这种关系。

  • 您可以在用户文档中嵌入索引的“以下”数组。
  • 您可以拥有一个单独的“以下”文档集合,如下所示:

    {用户:ObjectID(“x”),以下:ObjectID(“y”)}

对于以下每个关系,您将在此集合中拥有一个文档。您需要在此集合上有两个索引,一个用于“用户”,一个用于“关注”。

请注意,您问题中的第二个建议(在用户文档中包含“关注”和“关注”的数组)只是第一个的变体。

2) 正确的设计取决于您在这里没有提到的几个因素。

  • 一个人可以有多少粉丝,一个人可以关注多少人?
  • 您最常见的查询是什么?是展示关注者列表,还是展示被关注用户列表?
  • 您多久更新一次关注者/关注列表?

3) 取舍如下:

嵌入式数组方法的优点是代码更简单,您可以在单个文档中获取整个关注用户数组。如果您索引“关注”数组,那么查找所有用户关注者的查询将相对较快,只要该索引完全适合 RAM。(这与关系数据库没有什么不同。)

如果您经常更新关注者,或者如果您允许无限数量的关注者/关注者,则会出现嵌入式数组方法的缺点。

如果您允许无限数量的追随者/追随者,那么您可能会溢出 MongoDB 文档的最大大小。对于某些人来说,拥有 10 万或更多的追随者并非闻所未闻。如果是这种情况,那么您需要采用单独收集方法。

如果您知道关注者会经常更新,那么您可能也希望使用单独收集方法。原因是每次添加关注者时,都会增加“关注者”数组的大小。当它达到一定的大小时,它将超过为它在磁盘上保留的空间量,MongoDB 将不得不移动文档。这将产生额外的写入开销,因为该文档的所有索引也必须更新。

4) 如果你想使用嵌入式数组方法,你可以做一些事情来使它更可行。

首先,您可以限制一个人可以拥有的关注者总数。其次,当你创建一个新用户时,你可以创建一个预先创建大量虚拟关注者的文档。(例如,您使用大量您知道不涉及任何实际用户的条目填充 'followers' 数组 - 可能是 ID 0。)这样,当您添加新的关注者时,您会替换其中一个 ID 0 条目与真实条目,并且文档大小不会增长。

其次,您可以限制某人可以拥有的关注者数量,并在应用程序中检查。

请注意,如果您在文档中使用双数组方法,您将减少一个人可以拥有的最大关注者数量(因为文档的一部分将被他们关注的用户数组占用)。

5)作为一种优化,您可以更改要分桶的“关注”文档。因此,您可以按用户对它们进行分桶,而不是为每个关注关系一个文档:

   { user: "X", following: [ "A", "B", "C" ... ] }
   { user: "X", following: [ "H", "I", "J" ... ] } 
   { user: "Y", following: [ "A", "X", "K" ... ] } 

6) 有关多对多建模方法的更多信息,请参阅此演示文稿:

有关“分桶”设计模式的更多信息,请参阅 MongoDB 文档中的此条目:

于 2012-07-11T23:35:58.173 回答
0

如果您同时提供两者followersfollowees那么您可能可以有效地为大多数查询提供服务,而无需在这些字段中的任何一个上使用二级索引。例如,您可以检索当前用户,然后使用 _id 上的默认索引来检索他们所有连接的列表。

db.users.find({_id: {$in: user_A.followers}})

如果不包含followees,则需要创建二级索引followers以便在不进行集合扫描的情况下为某些查询提供服务。例如,要确定用户 A 的所有关注者,您可以使用如下查询:

db.users.find({followers: user_A._id})

二级索引会花费您一些内存和磁盘空间,但可以避免潜在的数据不一致(关注者和关注者列表不匹配)。

于 2012-07-11T19:22:49.983 回答