PostgreSQL触发器需用IS DISTINCT FROM显式比较字段新旧值以正确处理NULL,NOTIFY仅支持轻量异步通知且不保证送达,触发器内严禁耗时操作,UPDATE未触发常因字段未实际变更或条件判断失效。

触发器怎么判断某个字段真的变了
PostgreSQL 触发器本身不自动对比新旧值,NEW 和 OLD 是两个独立的记录,必须显式比较。比如监听 status 字段变化,不能只写 IF NEW.status IS DISTINCT FROM OLD.status 就完事——要注意 NULL 的语义:用 IS DISTINCT FROM 而不是 !=,否则 NULL != NULL 返回 NULL(即 false),导致变更被漏掉。
常见错误是直接写 IF NEW.status != OLD.status,结果 status 从 'pending' 变成 NULL 或反过来时,触发器完全不执行。
-
IS DISTINCT FROM安全处理 NULL,推荐作为默认比较方式 - 如果字段类型支持,也可用
COALESCE(NEW.status, '') != COALESCE(OLD.status, ''),但需确保替换值不与业务值冲突 - 对 JSONB、ARRAY 等复杂类型,
IS DISTINCT FROM同样适用,无需额外序列化
用 NOTIFY 发送通知时要注意什么
NOTIFY 是轻量级异步消息机制,但它不带 payload,只能传一个 channel 名和可选字符串(PostgreSQL 14+ 支持第二个参数,但仅限文本)。所以别指望靠它直接把新旧值塞过去——得靠外部监听程序自己查表或依赖上下文。
典型做法是:触发器里只发固定 channel 名(如 'order_status_change'),附带主键 ID:NOTIFY order_status_change, NEW.id::text;监听端收到后,再根据 ID 查询当前行快照。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- channel 名必须是合法标识符(不能含空格、特殊符号),建议全小写加下划线
- 第二个参数最大 8000 字节,超长会被截断,不适合传大字段或 JSON
-
NOTIFY不保证送达,也不排队——如果监听端断连期间发了 10 条,重连后只收到最新一条(除非你自己实现队列)
触发器函数里能不能做耗时操作
不能。触发器函数运行在事务上下文中,任何阻塞操作(如 HTTP 请求、文件写入、长时间查询)都会拖慢甚至卡死整个事务。想调用外部服务?必须异步化。
安全做法只有两种:
一是用 pg_notify() 发通知,让外部 worker 拉取处理;
二是用 pg_background 扩展(需额外安装)或写入一张任务表,由独立进程轮询。
- 绝对不要在触发器里调
curl、http_post()或COPY TO - 即使只是
RAISE NOTICE,在高并发写入场景下也可能成为性能瓶颈 - 如果必须记录日志,优先写到
INSERT INTO audit_log表,而不是打到数据库日志文件
为什么 UPDATE 语句没走触发器
最常见原因是触发器定义在 AFTER UPDATE,但 SQL 语句实际没修改任何字段——比如 UPDATE users SET updated_at = NOW() WHERE id = 123,而触发器只监听 email 字段,此时 OLD.email = NEW.email,触发器逻辑里判断条件不成立,自然跳过。但这不代表触发器没运行,它其实执行了,只是没进分支。
- 用
pg_trigger_depth()检查是否嵌套触发(防止无限递归) - 在触发器开头加
RAISE LOG 'trigger fired: % -> %', OLD.status, NEW.status;快速验证是否被调用 - 注意
UPDATE ... SET col = col这种赋值,虽然值没变,但会触发BEFORE/AFTER触发器(除非加WHEN (OLD.* IS DISTINCT FROM NEW.*)条件)
触发器逻辑越薄越好,字段比较、NOTIFY、简单审计写入可以放里面;状态校验、跨服务通信、格式转换这些,交给监听端更稳。最容易被忽略的是 NULL 比较和 NOTIFY 的无状态特性——你发出去的不是“事件”,只是一个“请检查”的信号。

















