Postgres 队列如何突破局限?每秒 30000 次工作流执行的优化秘籍!
【DBOS 产品与资源介绍】
可点击链接查看产品,如 DBOS Transact(开源持久执行库)、DBOS Conductor(代理与工作流的控制平面)。也能点击链接查看更多信息,包括定价、客户案例等。资源链接涵盖关于 DBOS、视频、合作伙伴等相关内容;文档链接有快速入门、详细文档、示例应用程序等。还有博客可探索,以及更多持久执行库相关仓库。7 月 24 日有 DBOS 用户组会议。产品还有 DBOS Cloud(一键部署,扩展到数百万规模)。
【Postgres 队列能否扩展?】
通常认为基于 Postgres 的队列无法扩展,处理大规模工作负载需采用专门队列系统,如 RabbitMQ + Celery 或 Redis + BullMQ,因为队列对 Postgres 是具有挑战性的工作负载,大规模场景下会引发争用和索引频繁更新。但通过正确优化,Postgres 也能应对挑战,可实现每秒 30000 次工作流执行,且能跨数千台服务器运行。
【经验一:重新发现 SKIP LOCKED】
要让基于 Postgres 的队列正常工作,需解决多个工作进程出队相同工作流时的争用问题。基于 Postgres 的队列工作方式是客户端将工作流入队,工作进程出队并处理最早入队的工作流。多个工作进程同时运行查询会产生争用,成为系统瓶颈。幸运的是,Postgres 提供锁定子句解决此问题,如使用 `FOR UPDATE SKIP LOCKED` 的查询,它能锁定行并跳过已锁定的行,使许多工作进程可在无争用情况下同时拉取新工作流。锁定子句使基于 Postgres 的队列成为可能,但实现更大规模扩展还需更多优化。
【经验二:注意事务隔离级别】
锁定子句虽提高了性能,但在大规模场景下,出队操作常因 Postgres 的“序列化失败”异常而失败,形成性能瓶颈。问题根源在于 Postgres 的事务隔离级别,最初出队事务以 `REPEATABLE READ` 级别运行以支持全局队列限制,但在高并发情况下成本很高,工作进程花费在重试事务上的时间比处理工作流的时间还多。关键发现是大型队列很少使用全局流量控制,用户通常更喜欢本地限制。因此,使隔离级别具有条件性,使用全局流量控制的队列继续使用 `REPEATABLE READ`,不使用的队列则使用 `READ COMMITTED`,消除了序列化失败并提高了吞吐量。
【经验三:索引并非免费】
有了锁定子句和较低的隔离级别后,争用问题几乎消失,但每秒运行超过 8000 个工作流时,会遇到高 CPU 使用率的新瓶颈,问题来自出队查询本身和 Postgres 的自动清理,根本原因是低效的索引。工作流状态表上的二级索引在大规模场景下效率低下,出队索引需额外排序增加 CPU 使用率,维护多个索引成本高,索引更新和清理会消耗大量数据库 CPU 资源。解决方案是使索引更具选择性,更新主要出队索引,使其能排序并转换为部分索引,同时将同样原则应用于大多数可观测性索引。综合这些优化,CPU 使用率大幅降低,使队列能扩展到每秒超过 30000 个工作流。
【了解更多与分享】
若热衷于构建可扩展、可靠的系统,可查看快速入门、GitHub 及 Discord 社区。还有近期文章介绍了持久执行、AI 工作流等方面的新特性。文章还提供了分享到领英、推特、脸书及通过邮件分享的链接。
【DBOS 产品与解决方案】
DBOS 极大地简化了云应用程序的运维和部署。产品有 DBOS Cloud、DBOS Transact、定价计划等;解决方案包括定时任务平台、持久 AI 工作流、持久数据管道、云现代化等;开发者资源有文档、快速入门指南、示例、教程等;还有公司信息如关于我们、隐私政策等。
【开始使用 DBOS】
可使用开源的 DBOS Transact 库,永久免费,也可搭配 DBOS Pro 获取高级工具和支持。还可订阅 DBOS 洞察获取相关更新,产品有 DBOS Transact、DBOS Conductor 等,有多种用例和客户案例,也提供开发者资源和公司相关信息。