别再用 Redis 了,Postgres 队列也能扩展
Making Postgres queues scale

传统观点认为 Postgres 不适合处理高并发任务队列,但 DBOS 团队通过实践打破了这一认知。文章展示了如何利用 Postgres 的 ACID 特性构建可扩展的作业队列,无需引入 Redis 或 Kafka 等额外组件。通过优化事务处理和并发控制,Postgres 队列在性能上表现优异,能够轻松应对大规模并发场景。这不仅简化了技术栈,还提升了系统的可靠性。对于追求架构简洁和稳定性的开发者来说,这是一个值得尝试的新思路。
我们证明了 Postgres 队列实际上可以扩展,无需依赖 Redis 或 Kafka。
HN 评论区
33- atombender
DBOS 文章完全没有涉及的一个性能陷阱就是膨胀问题:如果你更新或删除已消费的行,由于 Postgres 的 MVCC 机制,死元组(dead tuples)会开始累积。
这是一个严重的问题,因为它会影响查询规划器做出正确选择的能力。死元组仍然被索引,而查询规划器并没有考虑到需要跳过它们,因此包含大量死元组的表性能可能会非常差。autovacuum 进程会不断追赶死元组,你需要将 autovacuum 设置得非常激进才能跟得上。
我的团队开始在新应用中使用 PgQue [1],它的设计看起来非常出色。PgQue 明确旨在解决膨胀问题,方法是避免删除元组。它不会删除已处理的元组,而是定期 TRUNCATE 整个表。它使用两个表并在它们之间“切换”,这样可以在非活动表上运行 TRUNCATE,同时活动表继续用于队列操作。PgQue 还采用快照方法来避免行级锁。
PgQue 的另一个突出之处在于其队列模型是基于位置的,因此可以实现协作消费者、扇出(fan-out)、原子批处理和“从最后一个良好状态恢复”等优秀功能。它做出了一些妥协(不支持优先级、延迟略高),但对于大多数用例来说这些是可以接受的。
此前在 HN 上讨论过 [2]。
- dewey
> 关于 Postgres 支持队列的普遍看法是它们无法扩展。
那可能是 10 年前的情况。近年来出现了许多由 Postgres 驱动的队列系统,甚至 Rails 也在 3 年多前默认切换到了 Postgres 驱动的队列(SolidQueue)。
- sorentwo
难道“for no key update skip locked”不是比“for update skip locked”更好吗?大多数时候不会有改进,但当你不需要更强的锁时(例如 DELETE 操作),这是一个很好的选项。