追随者 - mongodb数据库设计

Ros*_*oss 9 javascript database-design mongodb database-performance database-schema

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

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

在此输入图像描述

我没有将关注者和项目添加到组集合中,因为组中可能有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);

  });

})
Run Code Online (Sandbox Code Playgroud)

是否有更好的DB模式来处理这种类型的设置?

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

任何帮助/建议将非常感谢.

非常感谢,Mac

mne*_*syn 11

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

MongoDB数据模型的关键是写入沉重,但这对于这个用例来说可能很棘手,主要是因为如果您想直接将用户链接到项目时需要进行簿记(更改为后续批次的组)用户会产生大量的写入,你需要一些工人来做这件事.

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

阅读重型方法

您主要关心的是以下用例:

一个真正的性能问题可能是当我想获得用户为特定项目关注的所有组时,因为那时我必须找到用户正在关注的所有组,并从中找到所有组具有group_id $in和item id 的item_groups .

让我们剖析一下:

  • 获取用户关注的所有组

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

  • 从中找到所有带有group_id $in和item id 的item_groups

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

不过,我们需要更深入了解:

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

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

那么超级节点怎么样?

由于这是猜测而且我对网络模型很感兴趣,我冒昧地实施了一个基于您的数据模型的大大简化的网络工具来进行一些测量.(对不起,这是在C#中,但是生成结构良好的网络在我最流利的语言中已经足够了......).

在查询超级节点时,我得到的结果是7ms tops(在1.3GB db中的12M条目,最大的组有133,000个项目,用户跟随143组).

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

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

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

写重法

替代方法可能是扭转链接的顺序:

UserItemLinker
{
 userId,
 itemId,
 groupIds[]  // for faster retrieval of the linker. It's unlikely that this grows large
}
Run Code Online (Sandbox Code Playgroud)

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

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