MongoDB Schema Design - 实时聊天

Posted

技术标签:

【中文标题】MongoDB Schema Design - 实时聊天【英文标题】:MongoDB Schema Design - Real-time Chat 【发布时间】:2011-02-25 13:37:38 【问题描述】:

我正在启动一个项目,我认为该项目特别适合 MongoDB,因为它提供的速度和可扩展性。

我目前感兴趣的模块是实时聊天。如果我要在传统的 RDBMS 中执行此操作,我会将其拆分为:

频道(一个频道有很多用户) 用户(一个用户有一个频道但有很多消息) 消息(消息有用户)

为了这个用例的目的,我想假设一次通常有 5 个通道处于活动状态,每个通道每秒最多处理 5 条消息。

需要快速的特定查询:

获取新消息(可能基于书签、时间戳或递​​增计数器?) 向频道发布消息 验证用户是否可以在频道中发帖

考虑到 MongoDB 的文档限制为 4mb,您将如何设计架构?你的会是什么样子?有什么我应该注意的问题吗?

【问题讨论】:

【参考方案1】:

我在聊天项目中使用了Redis、nginxphp-FPM。不是超级优雅,但它可以解决问题。这个难题有几个部分。

    有一个非常简单的 PHP 脚本,它接收客户端命令并将它们放入一个庞大的 LIST 中。它还检查所有房间列表和用户私人列表,以查看是否有必须传递的消息。这是由一个用 jQuery 编写的客户端轮询的,每隔几秒钟就会完成一次。

    有一个命令行 PHP 脚本,它以每秒 20 次的无限循环运行服务器端,它检查这个列表,然后处理这些命令。脚本在脚本内存中处理谁在什么房间和权限,此信息不存储在 Redis 中。

    Redis 为每个房间都有一个 LIST,每个用户都有一个 LIST,作为私有队列运行。对于用户所在的每个房间,它也有多个计数器。如果用户计数器小于房间中的消息总数,则它会获取差值并将其发送给用户。

我无法对这个解决方案进行压力测试,但至少从我的基本基准测试来看,它可能每秒可以处理数千条消息。还有机会将其移植到 Node.js 之类的东西上以提高性能。 Redis 也正在成熟,并且具有一些有趣的功能,例如 Pub/Subscribe 命令,这可能很有趣,可能会消除服务器端的轮询。

我研究了基于 Comet 的解决方案,但其中许多都很复杂,文档记录不完善,或者需要我学习一门全新的语言(例如 Jetty->Java、APE->C)等......还有交付和经历代理有时可能是 Comet 的一个问题。所以这就是我坚持投票的原因。

我想你可以用 MongoDB 做类似的事情。每个房间一个集合,每个用户一个集合,然后是一个维护计数器的集合。您仍然需要编写后端守护程序或脚本来处理这些消息的去向。您还可以使用 MongoDB 的“有限集合”,它可以保持文档排序并自动清除旧消息,但是维护适当的计数器可能会很复杂。

【讨论】:

【参考方案2】:

为什么将 mongo 用于消息传递系统?无论静态存储有多快(而且 mongo 非常快),无论是 mongo 还是 db,要模拟消息队列,您都必须使用某种轮询,这不是非常可扩展或高效的。诚然你没有做任何非常紧张的事情,但为什么不使用正确的工具来完成正确的工作呢?使用像Rabbit 或ActiveMQ 这样的消息传递系统。

如果你必须使用 mongo(也许你只是想玩弄它,而这个项目是一个很好的机会呢?)我想你会有一个用户集合(每个用户对象都有一个列表用户收听的队列)。对于消息,您可以为每个队列收集一个集合,但随后您必须轮询您感兴趣的每个队列以获取消息。最好将单个集合作为队列,因为在 mongo 中很容易对单个集合进行“in”查询,因此很容易执行“在任何队列中获取比 X 更新的所有消息”之类的事情.name 列表 [a,b,c]"。

您还可以考虑将您的集合设置为 mongo capped 集合,这意味着您在设置集合时告诉 mongo,您的集合应该只包含 X 个字节或 X 个项目。添加其他项目具有先进先出的行为,这对于消息队列来说非常理想。但同样,它并不是真正的消息传递系统。

【讨论】:

我不建议市面上的 MQ 解决方案确实比市面上的一些 NoSQL 解决方案好得多。很多 MQ 技术看起来很复杂和过度设计,而且性能并不总是那么好,稳定性和可移植性也可能会被牺牲。见:bhavin.directi.com/rabbitmq-vs-apache-activemq-vs-apache-qpid 那里有不错的 MQ 解决方案,我只是发现它们没有太多功能,ZeroMQ 和 Kestrel 都适合它们的用途。另一方面,ActiveMQ 是可怕的。 @Klinky 我敢打赌,几乎任何特定的 MQ 解决方案(尤其是 ActiveMQ)都会比基于未指定类型的 NoSQL 的自定义解决方案更好地处理消息传递 (EDA) 问题(您的意思是面向文档的数据库,或键值存储或什么?),因为 MQ 解决方案是针对该问题设计的,而且 FTN ActiveMQ 使用自己优化的高性能数据存储来实现队列持久性。 @Steve B. “...,这不是非常可扩展或高效的”——不同意“可扩展”(尽管同意效率和性能)。为什么?与将队列存储在内存中相反(这会导致问题,如果您的集群中有 1 个以上的节点——您需要设置复制或构建代理网络),让多个消费者在持久队列上工作似乎不太成问题(尤其是考虑到故障情况)。 @Klinky Twitter 开发人员做了很多奇怪的事情,你知道 :) (如果你有机会阅读 Twitter 的首席架构师之一关于 Scala 的书,你可能会猜到,如何“好”是他们的 MQ 解决方案)。关于 ActiveMQ——我个人对它有非常好的体验(我用它来构建一个巨大的分布式群发邮件系统)。约 30-60k/秒的吞吐量是使用一个代理的基本设置 - 如果您构建代理网络,性能可能会高出数倍。【参考方案3】:

1) 猿项目.org

2) http://code.google.com/p/redis/

3) 在您完成所有这些之后 - 您可以将数据转入 mongodb 以记录和存储一致的数据(用户、频道)

【讨论】:

以上是关于MongoDB Schema Design - 实时聊天的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB Schema Design - 许多小文档还是更少的大文档?

MongoDB 一对多关系建模

MongoDB对象之间的关系

MongoDB聚合时间序列

MongoDB、时间序列和聚合框架

如何在 MongoDB 中聚合时间序列数据