
Postgres LISTEN/NOTIFY 扩展性深度解析:单机 6 万次写入背后的优化之道
本文深入探讨了 Postgres LISTEN/NOTIFY 机制的扩展性争议。尽管该功能因全局锁导致的性能特性曾被认为难以扩展,但 DBOS 团队通过优化方案,在单台 Postgres 服务器上实现了每秒 6 万次写入及毫秒级延迟。相比于高延迟或高负载的传统轮询方案,LISTEN/NOTIFY 为 LLM 响应流等低延迟、高并发场景提供了更高效的持久化通知与发布/订阅解决方案。
核心要点
- 打破固有偏见:LISTEN/NOTIFY 长期以来因全局锁导致的性能问题被认为无法扩展,但实测证明通过优化可以实现高性能。
- 卓越性能表现:在单台 Postgres 服务器上实现了每秒 60,000 次写入,并保持毫秒级的延迟。
- 优于轮询机制:相比于会导致高延迟或数据库负载过重的轮询(Polling)方案,LISTEN/NOTIFY 能让读取者在数据到达时立即唤醒。
- 关键应用场景:该技术特别适用于需要低延迟、持久化通知的流式处理,如大语言模型(LLM)的逐字响应输出。
详细分析
挑战与误区:全局锁的性能瓶颈
Postgres 的 LISTEN/NOTIFY 功能在开发者社区中名声不一,很大程度上是因为其内部使用了全局锁,导致了一些非直观且未在文档中详述的性能特性。许多开发者认为这种机制在面对高并发写入时会成为系统的瓶颈。然而,DBOS 的研究指出,“非直观的行为”并不等同于“不可扩展”。通过深入理解其底层工作原理并进行针对性优化,可以克服这些限制,使其成为构建高性能实时系统的基石。
架构优化:实现低延迟流处理
在构建基于 Postgres 的流处理系统时,通常的做法是创建一个流表,将每个数据块(例如 LLM 生成的一个 Token)作为新行插入。读取端的挑战在于如何高效获知新数据的到来。传统的轮询方案存在两难境地:轮询间隔过长会导致延迟增加,影响交互体验;轮询间隔过短则会产生大量并发请求,使数据库过载。LISTEN/NOTIFY 机制通过允许读取者在等待通知时进入阻塞状态,解决了这一问题。当写入者发布新数据块时,读取者会被立即唤醒,从而在不浪费系统资源的前提下实现极低的响应延迟。
行业影响
这一发现对于实时数据驱动的应用具有重要意义。随着生成式 AI 的普及,LLM 的流式输出已成为标准需求。DBOS 的优化方案证明了开发者可以继续留在成熟的 Postgres 生态内,利用其原生的 LISTEN/NOTIFY 功能实现以往需要专门消息中间件才能达到的性能。这不仅简化了系统架构,还保证了通知与数据库事务之间的一致性和持久性,降低了运维复杂性。
常见问题
问题 1:为什么 LISTEN/NOTIFY 以前被认为难以扩展?
主要原因是它在处理通知时使用了全局锁,这在某些高并发配置下会导致严重的资源竞争。此外,其性能表现具有一定的非直观性,且缺乏详细的官方文档指导如何在大规模场景下进行优化。
问题 2:相比于轮询(Polling),LISTEN/NOTIFY 的核心优势是什么?
轮询需要在延迟和数据库负载之间做权衡:低延迟意味着高频率查询,会压垮数据库;高延迟则影响用户体验。LISTEN/NOTIFY 采用事件驱动机制,读取者仅在有新数据时才被唤醒,既保证了毫秒级延迟,又极大地减轻了数据库的无效查询压力。


