为什么我们需要像RabbitMQ这样的消息代理而不是像PostgreSQL这样的数据库?

265

我对像RabbitMQ这样的消息代理系统还不熟悉,我们可以使用它来创建任务/消息队列,用于类似Celery这样的调度系统。

现在有一个问题:

  • 我可以在PostgreSQL中创建一个表,该表可以附加新任务,并由类似Celery的消费程序使用。

  • 那么,为什么要设置一个全新的技术,如RabbitMQ呢?

现在,我相信扩展性不能是答案,因为我们的数据库像PostgreSQL一样可以在分布式环境中工作。

我在Google上查找了数据库对特定问题的影响,发现以下问题:

  • 轮询使数据库忙碌且性能低下
  • 锁定表->再次性能低下
  • 数百万行的任务->同样,轮询性能低下

那么,RabbitMQ或其他类似的消息代理如何解决这些问题呢?

另外,我发现它遵循AMQP协议。这有什么好处?

可以将Redis用作消息代理吗?我发现它比RabbitMQ更类似于Memcached。

请解释一下!


12
PostgreSQL 实现了 MVCC(多版本并发控制),因此读者和写者之间不会相互阻塞,所以锁定的影响应该要小得多。大多数批评将数据库用作消息队列的文章都是针对 MySQL 的。 - CadentOrange
消息代理在节点之间传输数据,而数据库将数据保存在一个地方。事实上,你可以从多个节点访问数据库中的数据,并不意味着它是一个快速传输数据的好工具。 - theMayer
2
类似于celery的调度系统——我从这个问题中学到了一些有用的设计知识,现在该看看答案了... - Mark K Cowan
1
使用消息代理生产者和消费者是解耦的。 - giorgi dvalishvili
您可以查看下面的链接。它有一个广泛的描述: https://stackoverflow.com/a/51377756/3073945 - Md. Sajedul Karim
@CadentOrange 你的评论可能会给读者留下MySQL性能比PostgreSQL差的印象,但这是相当误导人的。一般来说,MySQL也不应该有这样的问题(指的是InnoDB,自2010年起成为默认存储引擎)。同样适用于其他流行的数据库,如MS SQL Server、Oracle DB等。现在,这些关系型数据库之间存在许多细节和差异,可能对性能产生一些影响,但这些无法用几句话概括。 - at54321
2个回答

143
RabbitMQ的队列存储在内存中,因此比在数据库中实现要快得多。一个(好的)专用消息队列还应提供基本的排队相关功能,例如流量控制和选择不同路由算法的能力(RabbitMQ提供这些和更多)。根据项目的大小,您可能还希望将消息传递组件与数据库分开,以便如果一个组件经历重负载,它不会妨碍其他组件的操作。
至于你提到的问题:
  • 轮询导致数据库繁忙和性能低下:使用Rabbitmq,生产者可以向消费者推送更新,这比轮询更有效率。当需要发送数据时,它只需简单地将数据发送给消费者,从而消除了不必要的检查。

  • 表锁定 -> 再次导致性能低下:没有表需要锁定 :P

  • 数百万行任务 -> 再次轮询会降低性能:如上所述,Rabbitmq 将在 RAM 中运行得更快,并提供流量控制。如果需要,它还可以使用磁盘临时存储消息,以防内存不足。在2.0版本之后,Rabbit 的RAM使用情况显著改善。集群选项也可用。

关于AMQP,我认为真正酷的功能是“交换机”,以及它路由到其他交换机的能力。这为您提供了更多的灵活性,并使您能够创建各种复杂的路由拓扑结构,在扩展时非常有用。一个很好的例子,请参见:


(来源:springsource.com)

另外,关于Redis,它可以用作消息代理,并且表现良好。但是,Rabbitmq比Redis具有更多的消息排队功能,因为Rabbitmq从头开始构建为一个功能齐全的企业级专用消息队列。另一方面,Redis主要是为了成为内存中的键值存储而创建(尽管现在它做得更多了;甚至被称为瑞士军刀)。尽管如此,我已经阅读/听到许多人在较小规模的项目中使用Redis取得了良好的结果,但在较大的应用程序中并没有听说太多。

并且:http://blog.springsource.org/2011/04/01/routing-topologies-for-performance-and-scalability-with-rabbitmq/

这里是Redis在长轮询聊天实现中的示例:http://eflorenzano.com/blog/2011/02/16/technology-behind-convore/

3
我已经在数据库上实现了JMS(即消息传递系统)的实现。可以告诉你,这是可行的,但并不好玩,而且通常做不到付出相应的回报。你提到的一些问题可以解决,但会增加复杂度。总的来说,我同意:如果需要,使用专用的MQ系统。但是对于低工作量,可以将其放在数据库中。 - Joachim Sauer
1
你简单明了地解决了所有的疑虑和问题。非常棒的回答! - Yugal Jindle
31
实际上,使用PostgreSQL时不存在轮询(请参见NOTIFY),也不存在表锁(请参见MVCC)。虽然PostgreSQL仍未专门设计为消息队列,但它并非完全不适用。 - jkj
4
就像@jkj说的那样,有NOTIFY而没有表锁。唯一的问题似乎是消息带宽较高。您可以使用专用的PostgreSQL实例,而不是维护一个全新的Rabbit系统。您可以1)使用单个PostgreSQL实例,直到达到瓶颈,然后2)使用专用的Postgres,最后3)轻松切换到Rabbit作为您的代理程序。看起来从Rabbit开始是过度优化。 - Joe
1
链接 http://eflorenzano.com/blog/2011/02/16/technology-behind-convore/ 已经失效。 - Luk Aron
显示剩余6条评论

84

PostgreSQL 9.5

PostgreSQL 9.5引入了SELECT ... FOR UPDATE ... SKIP LOCKED。这使得实现工作排队系统变得更加简单易用。现在,只需获取未被其他会话锁定的'n'个行并将其保持锁定状态,直到确认完成工作即可。如果需要外部协调,则可以使用两阶段事务。

虽然外部排队系统仍然有用,可以提供预定义功能、经过验证的性能、与其他系统的集成、水平扩展和联合等选项。但是,对于简单情况,你不再真正需要它们。

旧版

你不一定需要这些工具,但使用它们可能会让生活更轻松。在数据库中进行排队看起来很简单,但实践证明,在关系型数据库中实现高性能、可靠的并发排队确实非常困难。

这就是为什么像 PGQ 这样的工具存在的原因。

你可以通过使用LISTENNOTIFY来摆脱在PostgreSQL中使用轮询的问题,但这并不能解决以下问题:如何可靠地将队列中的条目分配给仅一个消费者,同时保持高并发操作且不阻塞插入。你认为的所有简单和显而易见的解决方法,在实际情况下都无法解决这个问题,并且倾向于退化为不太高效的单工作器队列获取版本。

如果不需要高度并发的多工作器队列获取,则在PostgreSQL中使用单个队列表完全是可以接受的。


13
这句话的意思是,“可靠地将队列顶部的项交给一个消费者,同时保持高度并发操作且不阻塞插入”。这句话已经很简洁地概括了整个内容,对吗? - Yugal Jindle
Celery使用Postgres的这个特性吗?如果不是,那么这并没有什么帮助。 - duality_
如果它不行,就编写一个补丁 :) - Craig Ringer

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