MongoDB数据库模式设计

12

我有一个拥有50万用户的网站(运行在SQL Server 2008上)。我现在想要包含用户和他们朋友的活动流。在尝试了一些SQL Server的东西后,显然关系型数据库不适合这种功能,即使我大量去规范化数据也很慢。因此,在查看其他NoSQL解决方案后,我发现可以使用MongoDB来实现。我将遵循基于activitystrea.ms的数据结构 活动流json格式说明 所以我的问题是:对于MongoDB中的活动流,哪种是最佳的模式设计(由于有这么多用户,你可以预测它将非常依赖写入,因此我选择了MongoDB——它具有出色的“写入”性能)。我考虑了3种类型的结构,请告诉我是否合理或者我应该使用其他模式设计。

1 - 在同一个文档中存储每个活动和所有好友/关注者:

 
{ _id:'activ123', actor:{ id:person1 }, verb:'follow', object:{ objecttype:'person', id:'person2' }, updatedon:Date(), consumers:[ person3, person4, person5, person6, ... so on ]
}

2 - 第二种设计:集合名称-activity_stream_fanout

    {
    _id:'activ_fanout_123',
    personId:person3,
    activities:[
    {
     _id:'activ123',
     actor:{
            id:person1
            },
    verb:'follow',
    object:{
            objecttype:'person',
            id:'person2'
            },
    updatedon:Date(),
    }
],[ //activity feed 2 ]
}

3 - 这个方法是将活动项目存储在一个集合中,而消费者则存储在另一个集合中。在活动中,您可能会有一个类似以下的文档:

    { _id: "123",
      actor: { person: "UserABC" },
      verb: "follow",
      object: { person: "someone_else" },
更新时间:Date(...)

然后,对于关注者,我将有以下“通知”文档:

    { activityId: "123", consumer: "某人", 更新时间:Date(...)}
    { activityId: "123", consumer: "其他人", 更新时间:Date(...)}
    { activityId: "123", consumer: "第三个人", 更新时间:Date(...)} 

非常感谢您的提问。

2个回答

20
我建议采用以下结构:
  1. 使用一个集合来存储所有发生的操作,名为 Actions

  2. 使用另一个集合来存储关注关系,名为 Subscribers

  3. 使用第三个集合,名为 Newsfeed 来存储某个用户的新闻源,其中的条目从 Actions 集合中推出。

由异步处理新的 Actions 的工作进程填充 Newsfeed 集合。因此,新闻源不会实时更新。我不同意 Geert-Jan 的观点,即实时性很重要;我认为大多数用户在大多数(而非全部)应用程序中甚至不会在一分钟内关注到延迟(对于实时性,我会选择完全不同的架构)。

如果有大量的 consumers,则扇出可能需要一段时间。另一方面,将消费者直接放入对象中也无法适用于非常庞大的关注者数量,并且会创建过度庞大的对象,占用大量索引空间。

最重要的是,扇出设计更加灵活,可以进行相关性评分、过滤等操作。我最近写了一篇关于使用MongoDB设计新闻源模式的博客文章,在其中详细解释了这种灵活性。
说到灵活性,我会谨慎考虑activitystrea.ms规范。它似乎是用于不同提供商之间的互操作的规范,但如果您不打算汇总来自各种应用程序的活动,则不要将所有冗长的信息存储在数据库中。

很好的建议。我所说的实时并不是指亚秒级,而是指足够快,以至于在方案2中批处理多个用户活动不会带来太多好处。然而,我不熟悉“扇出”这个术语(似乎OP的第二个选项也涉及到了这个术语,你也提到了),所以我可能没有完全理解2的意图。另外,顺便说一下:我要去读一下那篇博客文章,总是很好看到关于MongoDB模式设计的架构文章。 - Geert-Jan
非常棒的阅读,我在你的博客上留了一个相关问题的评论,你可能想看一下。谢谢。 - Geert-Jan
1
大家好,非常感谢你们的建议。我将@mnemosyn的帖子标记为答案,因为它确实有意义。我会阅读你们的博客,看看能带给我什么启示。再次感谢你们提供的所有建议。 - Michael Simmons
@mnemosyn:在这个设计中让我感到担忧的是“新闻动态”集合会迅速增长。假设我们只有1000个注册用户,每个用户被10个用户关注,每天进行10次操作。那么每10天,“新闻动态”集合就会增加100万条记录。请告诉我如何处理这个问题。 - Roman
@mnemosyn:我也很想知道你对oyatek上面提到的“大量收集”问题的看法。在此期间,您有什么经验/观察可以分享的吗? - vaiomike
1
大家好,看来我错过了你们的评论 :-( 删除旧帖子是有意义的 - 大多数新闻源不允许您往回查看太久。Facebook在其时间线功能中使用了大量缓存。您无法在RAM中保留所有用户的所有活动的详细日志。难点在于高效地删除旧内容。另一种方法是为每个用户使用预分配的空列表,再次将每个用户的条目最大大小保持恒定。但即使是每个用户的80b的1M条目和两个服务器副本集,也需要160M RAM或大约每个用户每月0.07美分才能在RAM中保留最后1000条记录。 - mnemosyn

1

我认为你应该考虑你的访问模式:你最可能在这些数据上执行哪些查询等。

对我而言,需要最快速度的用例是能够将某个活动推送到每个“活动消费者”的“墙”(以fb术语表示),并在活动发生时立即执行。

从这个角度来看(我没有多想),我会选择1,因为2似乎会将某个用户的活动分批处理?因此无法满足更新的“即时”需求。此外,我不认为3在这种情况下比1更具优势。

一些1的增强措施?问自己是否真的需要定义每个活动的消费者数组的灵活性。是否真的有必要在这个细粒度的层面上进行指定?相反,对“演员”的“朋友”的引用是否足够?(随着消费者通常在数百人(?)范围内,这将节省很多空间。)

顺便提一下:根据您想要如何实现这些活动流的实时通知,值得考虑使用Pusher - http://pusher.com/和类似的解决方案。

希望对您有所帮助。


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