在PostgreSQL中实时捕获数据变更,通常可以在触发器记录和WAL日志解析两种思路之间做组合。触发器适合做业务内的轻量标记,WAL则适合做全量、低侵入的变更抽取。

一、触发器方式的基本做法
触发器可以在 insert、update、delete 发生时自动执行函数,把变更写入一张日志表。下面给出一个简单的触发函数示例:
CREATE TABLE IF NOT EXISTS user_log (
id serial primary key,
op_type text,
old_val jsonb,
new_val jsonb,
op_time timestamp default now()
);
CREATE OR REPLACE FUNCTION log_user_change()
RETURNS trigger AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO user_log(op_type, new_val)
VALUES('INSERT', to_jsonb(NEW));
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO user_log(op_type, old_val, new_val)
VALUES('UPDATE', to_jsonb(OLD), to_jsonb(NEW));
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO user_log(op_type, old_val)
VALUES('DELETE', to_jsonb(OLD));
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_user_change
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW EXECUTE FUNCTION log_user_change();
这种方式的优点是逻辑直观,能直接拿到前后镜像;缺点是每次写业务表都会多写一次日志表,高并发时容易成为瓶颈。
二、WAL与逻辑解码
WAL是PostgreSQL的预写日志,所有变更都会先写进WAL。通过开启逻辑解码,可以把WAL中的变更以可读形式输出。需要修改配置:
wal_level = logical max_wal_senders = 4 max_replication_slots = 4
之后创建一个复制槽并消费变更:
SELECT pg_create_logical_replication_slot('slot_demo', 'test_decoding');
SELECT * FROM pg_logical_slot_peek_changes('slot_demo', NULL, NULL);
三、Trigger结合WAL的使用策略
如果只靠触发器,性能压力集中在业务库;如果只靠WAL,很难在数据库内做复杂标记。推荐模式如下:
- 用触发器在关键表上写轻量标记,例如只记主键和变更类型
- 用WAL逻辑解码做全量捕获,供下游同步到数仓或消息队列
- 下游消费程序根据触发器的标记做过滤,减少无关处理
简单消费示例
import psycopg2
conn = psycopg2.connect("dbname=test user=postgres")
cur = conn.cursor()
cur.execute("SELECT * FROM pg_logical_slot_peek_changes('slot_demo', NULL, 10)")
for lsn, data in cur.fetchall():
# data类似: table public.users: INSERT: id[integer]:1 name[text]:'tom'
print(lsn, data)
cur.close()
conn.close()
四、注意事项
| 方案 | 侵入性 | 实时性 | 适用场景 |
|---|---|---|---|
| 触发器 | 高 | 秒级 | 小表审计 |
| WAL解码 | 低 | 毫秒到秒级 | 同步、CDC |
| 结合使用 | 中 | 秒级 | 复杂业务捕获 |
使用pg_logical_slot_get_changes会消费并清除槽中变更,而pg_logical_slot_peek_changes只查看不清除,实际生产常用前者避免WAL堆积。
触发器解决不了跨库实时同步,WAL才是PostgreSQL做外部捕获的正式通道。
五、小结
实时捕获PostgreSQL变更不必二选一。把触发器作为业务内辅助标记,把WAL逻辑解码作为主干管道,既能控制性能开销,也能保证变更不丢失。
PostgreSQLtriggerWAL修改时间:2026-07-28 08:33:13