不能在sql触发器里直接同步redis,因其同步执行、无网络能力、事务中禁止隐式提交;正确做法是mysql用binlog解析(如canal),postgresql用listen/notify异步通知,均由外部服务幂等处理缓存刷新。

不能在 SQL 触发器里直接同步 Redis——这不是“写法问题”,而是架构层面的硬限制:触发器同步执行、无网络能力、事务上下文中禁止隐式提交,任何 redis-cli、UDF 或 SYS_EXEC() 调用都会导致 DML 卡死、连接堆积或事务回滚。
MySQL 触发器调用 UDF 写 Redis 为什么必踩坑
哪怕你编译好了 mysql-udf-redis.so 并成功注册了 redis_set() 函数,在触发器里这么写:
SET @res = (SELECT redis_set(CONCAT('user:', NEW.id), JSON_OBJECT('id', NEW.id, 'name', NEW.name)));\n
也会立刻暴露三个不可控风险:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
redis_set()底层用hiredis建连,超时默认 2 秒 → 每次INSERT就卡 2 秒 - Redis 临时不可用 →
ERROR 1422: Explicit or implicit commit is not allowed直接让业务写入失败 - 主键变更(如
UPDATE SET id = 2001 WHERE id = 2000)会导致旧 key 残留、新 key 缺失,缓存永久错乱
PostgreSQL 的 LISTEN/NOTIFY 是唯一轻量可行路径
PostgreSQL 原生支持事务内安全发事件,不依赖外部进程,也不阻塞 DML。关键点在于它只发通知,不传数据:
- 触发函数里只调用
pg_notify('data_changes', payload::text),payload 控制在 8KB 内(推荐只传table_name、row_id、op_type) - 监听端用
LISTEN data_changes接收,收到后才去查 PostgreSQL 获取完整数据,避免触发器里做 JOIN 或 JSON 序列化 - worker 进程必须自己实现幂等:先
GETRedis 确认 key 是否已存在且时间戳未过期,再决定是SET还是跳过
MySQL 必须放弃触发器,转向 binlog 解析
MySQL 触发器在 ROW 格式下对批量更新可能漏触发,且无法感知 DDL 变更;真正生产可用的路径只有:
- 确保
binlog_format = ROW且binlog_row_image = FULL,否则字段缺失 - 用
Debezium或Canal解析 binlog,输出 JSON 到 Kafka 或直接 stdout - 下游服务消费事件时,用
table_name + row_id + op_type构造幂等 key(如users:123:update:1724719960),避免重复刷缓存 - 时间字段必须显式转 UTC:
UNIX_TIMESTAMP(CONVERT_TZ(NEW.updated_at, @@session.time_zone, '+00:00'))
最常被忽略的一点:Redis 同步不是“写完就完”,而是要覆盖所有异常路径——比如 worker 进程崩溃时,未确认的 binlog 位点必须可回溯,NOTIFY 消息丢失时要有兜底定时扫描。这些逻辑都不该、也不能塞进触发器里。










