2.9K 到 60K:被误解的轻量级消息队列
在分布式系统与实时应用架构中,PostgreSQL 的 LISTEN/NOTIFY 常被当成只能处理低频信号的辅助工具。很多架构师在遇到实时数据流或系统通知需求时,第一反应是引入 Redis 或 Kafka 等外部消息中间件。
这种选择往往来自于社区长期存在的认知:PostgreSQL 的内置通知机制无法应对高并发写入。在传统使用数据库触发器(Trigger)的实现中,单节点并发写入吞吐只能维持在 2,900 ops/sec 左右,一旦请求量激增就会引发连接积压与响应延迟。
图:Postgres LISTEN/NOTIFY 扩展性基准测试图示。来源:DBOS Blog
2026 年 7 月 24 日,DBOS 团队 CTO Peter Kraft 公布了一项基准测试数据。通过改动通知发送的架构模式,他们在保持 PostgreSQL 单节点部署的情况下,将 LISTEN/NOTIFY 的写入吞吐量直接提升到了 60,000 ops/sec,同时将延迟控制在 15 到 100 毫秒之间。
20 倍的性能提升开辟了新的设计空间,证明在许多中大型实时场景下,开发者不需要过早引入复杂的外部消息队列。 只要理解 PostgreSQL 内置通知的底层锁机制,就能在保留 ACID 事务特性的同时,获得接近专用消息系统的吞吐表现。
拿锁 fsync:排他锁拖慢 Group Commit
要理解性能瓶颈的来源,需要剖析 PostgreSQL 提交事务时对通知消息的处理流程。当一个事务执行 NOTIFY 时,系统并不会立刻向客户端广播消息,而是将通知内容暂存在事务本地的内存列表中。
真正的阻塞发生在事务提交阶段(即内核中的 Async_QueueCommitCode 过程)。为了保证所有订阅客户端能严格按照事务提交顺序接收消息,PostgreSQL 设置了一个全局排他锁 AsyncQueueLock。每个包含通知的事务在提交时,都必须抢占该锁才能将消息写入共享环形缓冲区。
图:每行触发器发送通知模式下的吞吐与延迟表现。来源:DBOS Blog
在默认的同步提交配置(synchronous_commit = on)下,事务在持有 AsyncQueueLock 的期间还需要等待预写日志(WAL)写盘(fsync)。这意味着其他并发事务无法进入提交队列,使得 PostgreSQL 极具价值的组提交(Group Commit)机制在此处失效。
测试数据清晰呈现了这一机制带来的副作用:随着并发连接数增加,数据库 CPU 使用率在 2,900 ops/sec 处快速饱和,绝大部分线程的时间被消耗在等待 AsyncQueueLock 的线程上下文切换上。全局排他锁与 WAL 磁盘刷盘的强绑定,是导致传统触发器通知模式无法扩展的物理原因。
状态归表,通知归内存:解耦顺序与持久性
DBOS 团队做出的突破性改变,核心思路在于重新审视 NOTIFY 消息的功能定位。在绝大多数业务系统中,通知本身不承担权威数据源(Source of Truth)职责,保存在数据库表中的业务记录决定了最终状态。
图:内存缓冲与批量 flush 通知机制的架构流程。来源:DBOS Blog
既然权威数据已经通过常规事务持久化在数据表中,通知消息就不再需要承受物理级别的事务强持久性保障。DBOS 移除掉了行级数据库触发器,改由应用进程在将数据批量写入数据库后,在内存队列中缓冲通知载荷(Payload)。
应用层的后台任务会按时间间隔(例如每 10 毫秒)或按积累数量将多条通知合并,只触发一次 NOTIFY 调用。这使数据库全局排他锁的竞争频率直接降低了几个数量级,单个事务处理的消息体量增长了数十倍。
图:采用批量缓冲模式后的吞吐提升曲线。来源:DBOS Blog
将高频排他锁竞争转化为内存中的合并批处理,以数十毫秒的极微小延迟换取了 20 倍的吞吐飞跃。 这种解耦设计保留了 Postgres 的易用性,消除了单条消息提交带来的磁盘等待开销。
轮询补偿与 PG19 补丁:工程落地中的权衡
任何将数据操作从数据库下放或移至内存的方案,都需要面对极端异常下的数据一致性挑战。由于 DBOS 方案将未发送的通知保存在应用进程内存中,一旦应用节点遭遇宕机崩溃,缓冲区中的通知便可能丢失。
图:基于低频轮询的信号丢失恢复机制。来源:DBOS Blog
为了解决异步通知丢失的问题,系统在订阅端设计了低频轮询恢复机制。订阅者在收到通知后会记录已处理的自增序列号,如果发现序列号出现跳跃或者长时间没有收到新通知,就会自动触发一次对底层数据库表的直接查询。
这种「通知作提示,查询做保底」的组合拳,确保了系统在极端异常下依然满足最终一致性。与此同时,PostgreSQL 社区也在内核层面探讨关于 AsyncQueueLock 的优化补丁,计划在 PostgreSQL 19 中改进多频道(Channel)下的锁争用情况。
内核补丁的重点在于隔离不同频道间的锁粒度,而应用层批处理解决了单频道高并发写入的瓶颈。 两者的侧重点不同,对于当前需要在生产环境落地高并发通知系统的团队而言,应用层的轻量级改动无需等待内核更新即可直接生效。
绕过数据库内核限制的架构思考
DBOS 团队开源的基准测试项目(dbos-inc/dbos-postgres-benchmark)为基础设施选型提供了一个清晰的视角。长时间以来,当现有工具遇到性能瓶颈时,团队往往倾向于通过叠加组件(如增加消息中间件)来解决问题。
每引入一个新组件,就意味着增加了运维复杂性、网络双跳延迟以及分布式事务的一致性隐患。在许多中等吞吐需求的业务场景中,数据库自身的内置功能并没有达到极限,只是默认的调用范式放大了内核锁竞争。
PostgreSQL 的 LISTEN/NOTIFY 机制在经过内存批量合并优化后,展现出了支撑每秒 6 万次操作的强劲能力。这提醒工程师在面临扩展性难题时,先分析底层锁机制与瓶颈成因,往往能用极小的代码改造换取巨大的性能收益。
系统的可扩展性由数据库内核实现与上层应用持久性边界共同决定。 当我们重新厘清权威状态与即时信号的分工,PostgreSQL 依然是那个能够兼顾简单性与高性能的基石。
参考链接:
- DBOS 官方技术博客
- Hacker News 讨论帖