我有一个拥有50万用户的网站(在sql server 2008上运行)。我现在想包括用户及其朋友的活动流。在SQL Server上测试了一些东西之后,很明显RDMS不是这种功能的好选择。它很慢(即使我严重非规范化了数据)。因此,在查看了其他NoSQL解决方案之后,我认为我可以使用MongoDB。我将遵循基于 activitystrea.ms 的数据结构活动流的 JSON 规范所以我的问题是:MongoDB 中活动流的最佳模式设计是什么(有这么多用户,你几乎可以预测它会非常繁重的写入,因此我选择了 MongoDB - 它具有很好的"写入"性能。我已经考虑了 3 种类型的结构,请告诉我这是否有意义,或者我应该使用其他模式模式。
1 - 以这种模式存储所有朋友/关注者的每个活动:
{ _id:"激活123", 演员:{ 编号:人1 }, 动词:"跟随", 对象:{ 对象类型:"人", ID:"人2" }, updatedon:Date(), 消费者:[ 人3, 人4, 人5, 人6, ...等等 ] }
2 - 第二种设计:系列名称 - activity_stream_fanout
{ _id:"activ_fanout_123", 人 ID:人3, 活动:[ { _id:"激活123", 演员:{ 编号:人1 }, 动词:"跟随", 对象:{ 对象类型:"人", ID:"人2" }, updatedon:Date(), } ],[ 活动源 2 ] }
3 - 此方法是将活动项存储在一个集合中,将使用者存储在另一个集合中。在活动中,您可能有一个文档,例如:
{ _id: "123", 演员: { 人: "用户ABC" }, 动词:"跟随", 对象: { 人: "someone_else" }, 更新日期: 日期(... }
然后,对于关注者,我将拥有以下"通知"文档:
{ activityId: "123", consumer: "someguy", updateOn: Date(...) } { activityId: "123", consumer: "otherguy", updateOn: Date(...) } { activityId: "123", consumer: "Thirdguy", updateOn: Date(...) }
非常感谢您的回答。
我会使用以下结构:
-
对发生的所有操作使用一个集合,
Actions
-
使用另一个集合来表示谁关注谁,
Subscribers
-
使用第三个集合,
Newsfeed
对于特定用户的新闻源,项目将从Actions
集合中扇出。
Newsfeed
集合将由异步处理新Actions
的工作进程填充。因此,新闻源不会实时填充。我不同意Geert-Jan的观点,即实时很重要;我相信大多数用户根本不关心大多数(不是全部)应用程序中哪怕一分钟的延迟(对于实时,我会选择完全不同的架构)。
如果您有非常多的consumers
,扇出可能需要一段时间,确实如此。另一方面,将消费者直接放入对象中也不适用于非常大的关注者数量,并且它将创建占用大量索引空间的过大对象。
然而,最重要的是,扇出设计更加灵活,允许相关性评分、过滤等。我最近刚刚写了一篇关于使用MongoDB进行新闻提要模式设计的博客文章,其中我更详细地解释了其中的一些灵活性。
说到灵活性,我会小心这个 activitystrea.ms 规格。作为不同提供程序之间的互操作规范似乎很有意义,但只要您不打算聚合来自各种应用程序的活动,我就不会将所有冗长的信息存储在我的数据库中。
我相信您应该查看您的访问模式:您可能对此数据执行最多的查询等。
对我来说,需要最快的用例是能够将某个活动推送到每个"活动消费者"的"墙"(以fb术语表示),并在活动进入时立即进行。
从这个角度来看(我没有多想)我会选择 1,因为 2. 似乎在处理某个用户之前为某个用户批处理活动?因此,如果失败,则"立即"需要更新。此外,我没有看到 3 的优势。对于此用例,超过 1。
1 的一些增强功能?问问自己,你是否真的需要为每个活动定义一组消费者的灵活性。真的有必要在这个细粒度的规模上指定这一点吗?相反,提及"演员"的"朋友"还不够吗?(从长远来看,这将占用大量空间,因为我认为消费者数组是每个活动的整个消息的大部分,而消费者通常范围为数百(?)。
有点相关的说明:根据您可能希望如何为这些活动流实现实时通知,可能值得查看 Pusher - http://pusher.com/和类似的解决方案。
呵呵