PostgreSQL触发器中必须用PERFORM pg_notify('channel','payload')发送通知,不可直接写NOTIFY命令;客户端需提前执行LISTEN且事务提交后才生效,通知仅在事务成功提交时异步投递。

触发器里用 pg_notify 发送通知,不是 NOTIFY
在触发器函数中不能直接写 NOTIFY channel, 'payload' —— 这是 SQL 命令,不能在 PL/pgSQL 函数体里执行。必须用函数形式:PERFORM pg_notify('channel', 'payload')。
常见错误是写成 EXECUTE 'NOTIFY ...',这会报错 ERROR: cannot execute NOTIFY in a function。因为 NOTIFY 不是可执行语句,而 pg_notify() 是内置函数,专为这种场景设计。
-
pg_notify第一个参数是通道名(text类型),必须是合法标识符;含空格或特殊字符需用双引号包裹,但不推荐 - 第二个参数是 payload,最大 8000 字节;超长会被截断,且不会报错
- 如果 payload 为空或
NULL,接收端收到的是空字符串,不是忽略该通知
LISTEN 必须在客户端侧提前执行,且事务提交后才生效
LISTEN channel 是客户端发起的命令,不是数据库自动做的。服务端不会“替你监听”。你在应用里连上 PostgreSQL 后,得先发一次 LISTEN mychannel,再持续轮询或等待通知到达。
关键点:这个 LISTEN 命令本身在事务内执行时,要等事务提交才真正注册成功。如果它被包在 BEGIN; LISTEN ...; ROLLBACK; 里,监听就无效。
- psql 中执行
LISTEN后,不会立刻显示“已监听”,要等下一条 SQL 执行完(比如SELECT 1)才会刷出Asynchronous notification "mychannel"... - 应用用 libpq 时,必须显式调用
PQnotifies()检查通知队列,不调就不会知道有新消息 - 监听状态只对当前连接有效;连接断开,自动
UNLISTEN所有通道
触发器 + LISTEN 的典型配合流程
这不是“数据库自动推给你”,而是“你主动注册 + 数据库按规则投递”的协作模型。漏掉任一环都会收不到。
- 先确保目标通道名一致:触发器里
pg_notify('orders'),客户端也LISTEN orders - 触发器必须定义在
AFTER INSERT OR UPDATE等能读取NEW的时机,BEFORE里NEW可能未完全构造好 - payload 建议用 JSON 格式(如
to_jsonb(NEW)),方便前端解析;避免拼接字符串引入 SQL 注入风险 - 不要依赖“第一次 LISTEN 就能收到历史通知”——PostgreSQL 不存通知历史,没监听时发的通知直接丢弃
容易被忽略的事务边界问题
触发器里的 pg_notify 调用,和它所在的事务强绑定。事务没提交,通知就不发;事务回滚,通知就取消。这点和普通 DML 一样,但容易误以为“只要进了触发器就一定发出去了”。
更隐蔽的是嵌套事务:子事务(SAVEPOINT)里调用 pg_notify,不会立即触发通知,而是等到最外层事务提交时才统一发出。如果你在子事务里发了多条,它们会按顺序合并到最终通知流中,但时间戳都一样。
- 想确认是否真发出了,可在同一会话里查
SELECT pg_listening_channels()看当前监听列表 - 用
UNLISTEN *清理所有监听,避免残留干扰调试 - 生产环境别用
LISTEN *(不存在该语法),PostgreSQL 不支持通配符监听

















