追随者 - mongodb数据库设计

时间:2015-02-09 23:32:13

标签: javascript mongodb database-design database-schema database-performance

所以我正在使用mongodb而且我不确定我是否已经为我正在尝试做的事情获得了正确/最佳的数据库集合设计。

可以有很多项目,用户可以创建包含这些项目的新组。任何用户都可以关注任何组!

enter image description here

我没有将关注者和项目添加到群组集合中,因为群组中可能有5个项目,或者可能有10000个(并且对于关注者来说相同),并且从研究中我认为您不应该使用未绑定的数组(由于尺寸不断扩大,文件必须移动时由于性能问题而导致限制未知)。 (在遇到性能问题之前,是否有建议的阵列长度最大值?)

我认为使用以下设计时,真正的性能问题可能是当我想获取用户为特定项目关注的所有组(基于user_id和item_id)时,因为我必须找到所有用户正在关注的组中,并从中查找具有group_id $ in和项ID的所有item_group。 (但我实际上看不到任何其他方式这样做)

Follower
.find({ user_id: "54c93d61596b62c316134d2e" })
.exec(function (err, following) {
  if (err) {throw err;};

  var groups = [];

  for(var i = 0; i<following.length; i++) {
    groups.push(following[i].group_id)
  }

  item_groups.find({
  'group_id': { $in: groups },
  'item_id': '54ca9a2a6508ff7c9ecd7810'
  })
  .exec(function (err, groups) {
    if (err) {throw err;};

    res.json(groups);

  });

})

是否有更好的数据库模式来处理此类设置?

更新:在下面的评论中添加了示例用例。

非常感谢任何帮助/建议。

非常感谢, MAC

4 个答案:

答案 0 :(得分:10)

我同意其他答案的一般概念,即这是临界关系问题。

MongoDB数据模型的关键是写入沉重,但这对于这个用例来说可能很棘手,主要是因为如果你想直接将用户链接到项目时需要记账(更改为一个组是其次是很多用户会招致大量的写作,而你需要一些工作人员这样做。)

让我们来研究读取重型模型是否在这里不适用,或者我们是否进行过早优化。

阅读重型方法

您关键的问题是以下用例:

  

真正的性能问题可能是当我想要获取用户为特定项目关注的所有组时,因为那时我必须找到用户正在关注的所有组,并且从那里找到包含group_id $in和商品ID的所有item_group。

让我们剖析一下:

  • 获取用户关注的所有群组

    这是一个简单的查询:db.followers.find({userId : userId})。我们将需要userId上的索引,这将使此操作的运行时间为O(log n),或者即使对于大n也是如此。

  • 从中找到包含group_id $in和商品ID

    的所有item_groups

    现在这是更棘手的部分。让我们暂时假设项目不可能成为大量群体的一部分。然后复合索引{ itemId, groupId }最有效,因为我们可以通过第一个标准显着减少候选集 - 如果一个项目仅在800个组中共享而用户在220个组之后,mongodb只需要找到交集这些都比较容易,因为两组都很小。

我们需要更深入地了解这一点:

您的数据结构可能 复杂网络的结构。复杂网络有很多种,但假设你的跟随图是nearly scale-free是有意义的,这也是最糟糕的情况。在无规模的网络中,极少数节点(名人,超级碗,维基百科)吸引了大量的“关注”(即有很多连接),而更多的节点难以获得相同的关注度结合

小节点无需担心,上面的查询(包括往返数据库的往返次数在我的开发计算机上的 2ms范围上)数以千万计的连接&gt; 5GB的数据。既然数据集不是很大,但无论你选择什么技术,都会受到RAM限制,因为索引必须在RAM中(数据局部性和网络中的可分性通常很差),并且设置的交集大小是根据定义很小。换句话说:这种制度主要是硬件瓶颈。

超级节点怎么样?

由于这是猜测而且我对网络模型很感兴趣,我took the liberty of implementing a dramatically simplified network tool根据您的数据模型进行一些测量。 (对不起,这是在C#中,但是生成结构良好的网络在我最流利的语言中已经足够了......)。

查询超级节点时,我得到 7ms tops 范围内的结果(这是1.3GB数据库中12M条目的结果,其中最大的组有133,000个项目以及跟随143组的用户。)

此代码中的假设是用户所遵循的组数量不是很大,但这在这里似乎是合理的。如果不是,我会采取重写的方法。

随意玩代码。不幸的是,如果你想用超过几GB的数据来尝试它,它将需要一些优化,因为它根本没有优化,并且在这里和那里进行一些非常低效的计算(特别是β加权随机混洗可以改进)。

换句话说:我不会担心重读方法的性能问题往往不是因为用户数量的增长,但用户以意想不到的方式使用系统。

写重法

替代方法可能是颠倒链接的顺序:

UserItemLinker
{
 userId,
 itemId,
 groupIds[]  // for faster retrieval of the linker. It's unlikely that this grows large
}

这可能是最具扩展性的数据模型,但除非我们讨论的是大量数据,其中分片是关键要求,否则我不会这样做。这里的关键区别在于,我们现在可以通过将userId用作分片键的一部分来有效地划分数据。这有助于在多数据中心场景中并行化查询,有效分片并改善数据局部性。

这可以通过更精细的测试版本进行测试,但我还没有找到时间,坦率地说,我认为对大多数应用程序而言都是过度杀伤。

答案 1 :(得分:3)

我读了你的评论/用例。所以我更新了我的答案。

我建议根据这篇文章更改设计:MongoDB Many-To-Many

设计方法不同,您可能希望重新构建您的方法。我会尝试给你一个开头的想法。 我假设用户和跟随者在这里基本上是相同的实体。 我认为您可能会感兴趣的一点是,在MongoDB中,您可以存储数组字段,这是我将用于简化/更正MongoDB设计的内容。

我要省略的两个实体是:关注者和项目组

  • 粉丝:这只是一个可以关注群组的用户。我会添加一个 组ID 的数组,以包含用户所遵循的组列表。所以我没有一个实体Follower,而只有User的数组字段有一个Group Ids列表。
  • ItemGroups:我也会删除这个实体。相反,我会在Group实体中使用Item Ids数组,在Item实体中使用Group Ids数组。

基本上就是这样。您将能够执行您在用例中描述的内容。从反映基于文档的数据库的设计决策的角度来看,设计更简单,更准确。

备注

  • 您可以在MongoDB中的数组字段上定义索引。例如,请参阅Multikey Indexes
  • 请注意在数组字段上使用索引。您需要了解您的用例,以确定它是否合理。见article。由于您只引用了ObjectIds,我认为您可以尝试它,但可能还有其他情况下更改设计更好。
  • 另请注意,ID字段 _id 是MongoDB 用作主键的ObjectID的特定字段类型。要访问ID,您可以参考它,例如as user.id,group.id等。您可以使用索引来确保此question的唯一性。

您的架构设计可能如下所示:

Schema design for MongDB (document database)

关于您的其他问题/疑虑

  

在遇到性能问题之前是否有建议的数组长度最大值?

答案是在MongoDB中,文档大小限制为16 MB,现在可以解决这个问题了。但是16 MB被认为是足够的;如果你达到16 MB,那么你的设计必须得到改进。有关信息,请参阅here,文档大小限制部分。

  

我认为使用以下设计时,如果我想获取用户为特定项目关注的所有组(基于user_id和item_id),则可能会遇到真正的性能问题...

我会这样做。注意&#34;更容易&#34;使用MongoDB时会发出声音。

  1. 获取用户的项目
  2. 获取引用该项目的组
  3. 如果数组变得非常大并且你在它们上使用索引,我会非常担心。这可以整体减慢对相应文档的写入操作。在你的情况下可能不是那么多,但不完全确定。

答案 2 :(得分:3)

您正在创建一个高性能的NoSQL架构设计,我认为您正在就如何正确布局提出正确的问题。

以下是我对您申请的理解:

看起来群组可以有很多关注者(将用户映射到群组)和许多项目,但是项目可能不一定在很多群组中(尽管可能)。从您给定的用例示例中,它听起来像检索项目所在的所有组,并且组中的所有项目都将是一些常见的读取操作。

在您当前的架构设计中,您已经实现了将用户映射到组作为关注者和将项目作为item_groups映射到组之间的模型。这可以正常工作,直到您提到更复杂的查询问题:

  

我认为通过以下设计,当我想要获取用户为特定项目关注的所有组(基于user_id和item_id)时,真正的性能问题可能是

我认为在这种情况下,有些事情可以帮助你:

  • 利用MongoDB强大的indexing capabilities。特别是,我认为您应该考虑在您的Follower对象上创建compound indexes,分别覆盖您的Group和User,以及Item_Groups。您还希望确保此类关系是唯一的,因为用户只能关注一次组,并且只能将一个项目添加到组中一次。最好在模式中定义的一些预保存挂钩中实现,或使用插件检查有效性。

FollowerSchema.index({ group: 1, user: 1 }, { unique: true }); Item_GroupsSchema.index({ group: 1, item: 1 }, { unique: true });

在写这些字段时使用索引会在写入集合时产生一些开销,但听起来像从集合中读取将是一种更常见的交互,因此它是值得的(我建议阅读更多关于index performance)。

  • 由于用户可能无法关注数千个群组,因此我认为在用户模型中包含用户所关注的群组数组是值得的。当您想要在用户当前关注的组中查找项目的所有实例时,这将帮助您解决该复杂查询,因为您将在那里拥有组列表。您仍然可以使用$in: groups进行实施,但是对该集合的查询较少。

  • 正如我之前提到的,似乎项目可能不一定在那么多组中(就像用户赢得了必须跟随数千个组一样)。如果案例通常可能是一个项目可能是几百个组,我考虑只是为项目模型添加一个数组,用于添加到的每个组。这会在读取项目所在的所有组时提高您的性能,您提到的查询将是常见的。注意:您仍然使用Item_Groups模型通过查询(现已编入索引的)group_id来检索组中的所有项目。

答案 3 :(得分:2)

不幸的是,NoSQL数据库在这种情况下不合格。您的数据模型似乎是精确的关系。根据MongoDB文档,我们只能these,并且只能执行these

some practices。 MongoDB建议我们使用Followers集合来获取哪个用户跟随哪个组,反之亦然,以获得良好的性能。您可以在幻灯片14上找到与您的情况on this page最接近的情况。但我认为如果您希望将每个结果放在不同的页面上,幻灯片可以符合条件。例如;您是Twitter用户,当您点击followers按钮时,您会看到所有关注者。然后,您点击关注者名称,您将看到关注者的消息以及您可以看到的任何内容。我们可以看到所有这些工作循序渐进不需要关系查询

  

我认为您不应该使用未绑定的数组(限制未知的地方),因为在扩展文档时必须移动文档时会出现性能问题。 (在遇到性能问题之前,是否有建议的阵列长度最大值?)

是的,你是对的。 http://askasya.com/post/largeembeddedarrays。 但是如果你的数组中有大约一百个项目就没有问题。 如果您有固定大小一些数据,您可以将它们作为数组嵌入到关系集合中。您可以快速查询索引的嵌入式文档字段。

以我的拙见,您应该创建数十万test data并检查使用适合您案例的嵌入式文档和数组的性能。别忘了创建适合您查询的索引。您可以尝试在测试中使用document references。经过测试,如果你喜欢结果的表现,请继续..

您曾尝试查找特定用户所遵循的group_id条记录,然后您尝试使用group_id找到特定项目。 Item_GroupsFollowers个集合是否可能有多对多关系? 如果是这样,NoSQL数据库不支持多对多关系。

您是否有可能将数据库更改为MySQL?

如果是这样,你应该检查this

briefly MongoDB pros against to MySQL;
- Better writing performance

briefly MongoDB cons against to MySQL;
- Worse reading performance

如果您使用Node.js,可以查看https://www.npmjs.com/package/mysqlhttps://github.com/felixge/node-mysql/

祝你好运......