LISTEN / NOTIFY · 数据库自带的发布订阅

PostgreSQL 内建一套轻量 Pub/Sub:一端 LISTEN 一个通道,另一端 NOTIFY 发消息,触发器里也能 pg_notify。消息随事务走——COMMIT 后才广播。缓存失效通知、轻量任务派发、库内事件钩子,不必再为此单独架一个 Redis。

作者 David更新于 第 14 篇(共 17 篇)

LISTEN / NOTIFY · 数据库自带的发布订阅

「数据变了,怎么让别的进程立刻知道?」——常规答案是架一个 Redis / Kafka 做消息广播,或者让应用不停轮询数据库。但如果你已经在用 PostgreSQL,它自带一套轻量的发布订阅,很多场景够用了,还免去多维护一个中间件。

三个动作:LISTEN、NOTIFY、pg_notify

机制简单到只有三个动作:

-- 订阅端:监听一个通道
LISTEN order_events;

-- 发布端:往通道发消息(可带 payload)
NOTIFY order_events, 'order 123 created';

-- 在函数 / 触发器里发,用函数形式(payload 可动态拼)
SELECT pg_notify('order_events', '{"id": 123}');

把它和触发器一拼,就是「数据一变,自动广播」:给表加个 AFTER INSERT 触发器,每插入一行就 pg_notify 一条。实测里正是这么做的:

当前连接 LISTEN ezdata_events
建表 + AFTER INSERT 触发器,每行插入广播一条
INSERT 三条 → 触发器广播 → 监听端实时收到:
  收到: {"id": 1, "msg": "hello"}
  收到: {"id": 2, "msg": "world"}
  收到: {"id": 3, "msg": "!"}

数据库的写入事件,就这样实时推到了应用端,全程没有轮询。

关键语义:跟着事务走

LISTEN/NOTIFY 有几条必须记住的语义,它们决定了它「靠谱在哪、不能用在哪」:

  • 随事务提交才广播:NOTIFY 发出的消息,要等所在事务 COMMIT 后才真正推给监听者。事务回滚,消息也不发。这点极好——你不会收到「其实没成功」的事件。
  • 同事务内相同消息合并:同一事务里对同一 (通道, payload) 发多次,监听者只收到一次。
  • payload 有大小限制:载荷不能超过 8000 字节。所以惯例是只发 ID / 事件类型,让应用收到后自己回查详情,而不是把大对象塞进 payload。

最要命的一条:LISTEN/NOTIFY 不持久化。消息发出时如果没有活跃的监听者,这条消息就永远丢了——它不排队、不重放、不保证送达。所以它只适合「丢了也不致命」的通知(缓存失效、UI 刷新提示),不适合当可靠消息队列(订单必达、支付回调)。要「至少一次送达」,得用真正的消息队列,或在库里建一张任务表 + SKIP LOCKED 轮询(见 锁与并发一章)。

它和 Redis Pub/Sub 差在哪

LISTEN/NOTIFY 常被拿来和 Redis 发布订阅比。两者都「不持久化、发出即忘」,但 PostgreSQL 版有个独特优势:和事务联动。

  • Redis pub/sub:你得先写数据库、再发 Redis 消息,两步之间可能「数据库写成功了但 Redis 挂了」,导致事件和数据不一致。
  • PostgreSQL NOTIFY:消息和数据在同一个事务里,COMMIT 一起成功、ROLLBACK 一起取消——数据和事件天然一致,不存在「事件发了但数据没落库」的裂缝。

代价是吞吐和扩展性不如专业消息中间件。所以它的定位很清楚:中小规模、要事务一致、不想多养一个组件时的轻量选择。

横向对比

能力PostgreSQL LISTEN/NOTIFYRedis Pub/SubKafka / RabbitMQ
是否内置数据库自带需 Redis需独立集群
与数据库事务一致是(COMMIT 才发)否(两步不原子)否
持久化 / 重放❌ 不持久❌ 不持久✅ 持久、可重放
吞吐 / 扩展中小高很高
运维成本零(已有 PG)一个 Redis一套集群

MySQL、Oracle、SQL Server 都没有这种「随事务提交的轻量广播」内置能力(Oracle 有更重的 DBMS_ALERT / 变更通知),这算 PostgreSQL 一个小而美的独特点。

应用场景

  • 缓存失效广播:某表更新 → 触发器 NOTIFY → 所有应用实例收到后清对应缓存,免掉「缓存和库不一致」的定时兜底。
  • 实时 UI 推送:后台数据变化通过 NOTIFY → WebSocket 推给前端,做「多人协作实时刷新」。
  • 轻量任务唤醒:任务表插入新行 → NOTIFY 唤醒 worker「有活了」,让 worker 从「定时轮询」变成「事件驱动 + 兜底轮询」,大幅降低空轮询。
  • 库内事件钩子:审计、指标、下游同步,用触发器 + NOTIFY 挂钩数据变更。

小结与下一章

LISTEN/NOTIFY 让 PostgreSQL 兼职一个「随事务一致的轻量消息总线」:数据变更能实时推给应用,还不必多养一个 Redis。记住它的边界——不持久、没监听者就丢,所以只做「丢了不致命」的通知,别当可靠队列。

说到「可靠队列」,PostgreSQL 其实能用另一套机制做出来——行级锁 + SKIP LOCKED。这就引出并发这一站的主题:锁。下一章讲咨询锁和行级锁,看 PostgreSQL 怎么兼任分布式锁和任务队列。👉 锁与并发:咨询锁与 SKIP LOCKED。

参考文献

本页目录