不推荐在MySQL触发器中调用UDF同步Redis,因其同步阻塞会导致事务卡死、网络异常引发写失败、高并发下线程堆积,且缺乏重试与监控;生产环境应采用binlog解析(如Canal)异步同步或触发器写中间表+外部服务轮询的伪异步方案。

不推荐在 MySQL 触发器里调用 UDF 同步 Redis,它会让事务卡死、写入失败、监控缺失,且高并发下极易拖垮数据库。
MySQL 触发器调用 UDF 写 Redis 会直接阻塞事务
触发器是同步执行的,一旦 UDF 中的 Redis 连接超时、网络抖动或服务不可用,INSERT/UPDATE/DELETE 就会卡住,直到超时或失败。这不是“慢一点”,而是业务写入直接不可用。
-
ERROR 1422: Explicit or implicit commit is not allowed in stored function or trigger—— 某些 MySQL 版本禁止在触发器中调用含隐式提交操作的 UDF - Redis 响应延迟 2 秒,你的订单插入就延迟 2 秒;若 Redis 宕机,
INSERT直接报错回滚,而你真正要保的其实是订单数据 - 所有 Redis I/O 都发生在
mysqld进程内,DBA 通常禁用此类插件(安全策略+稳定性要求)
UDF 编译注册本身就有多个硬性门槛
哪怕你坚持要试,mysql-udf-http 或自研 Redis UDF 也必须满足三者严格匹配:MySQL 版本、mysql_config 路径、plugin_dir 实际路径。漏一个就 CREATE FUNCTION 失败。
- 编译时必须指定正确插件目录:
--libdir=/usr/lib64/mysql/plugin/(不是/usr/lib64/mysql) - 生成的
.so文件名必须和SONAME完全一致,比如注册语句是CREATE FUNCTION http_post RETURNS STRING SONAME 'mysql-udf-http.so',那文件就得叫mysql-udf-http.so,不能是mysql-udf-http.so.0.0.0 -
plugin_dir权限需对mysqld用户可读,SELinux 开启时还可能拦截动态库加载,报Can't find symbol 'redis_set'
触发器里调用 UDF 的语法限制很具体
你不能在触发器里直接写 SELECT redis_set(...),MySQL 语法不允许;唯一合法写法是赋值给用户变量,再丢进子查询:
SET @res = (SELECT redis_set(CONCAT('user:', NEW.id), JSON_OBJECT('id', NEW.id, 'name', NEW.name)));
但这只是“语法通”,不代表逻辑稳:
- UDF 返回
NULL不报错,但缓存没写成,你完全感知不到 没有重试机制,一次失败就永久丢失
- Row-based binlog 下,某些批量更新可能不触发触发器(取决于
binlog_format和语句类型) - 主键变更(如
UPDATE SET id = 1002 WHERE id = 1001)会导致旧 key 残留、新 key 未写,缓存彻底错乱
真正能落地的替代方案只有两个方向
如果必须“数据一变,Redis 就更新”,就别让 MySQL 承担这个职责:
- 用
Canal或Debezium解析 MySQLbinlog(必须设binlog_format = ROW),消费端异步写 Redis —— 失败可重放、延迟可控、不影响主库 - 退一步,触发器只写
cache_sync_log表,外部服务轮询该表(如每 100ms 查一次),再批量推 Redis —— 准实时(延迟 ≤ 1s),但解耦、可控、易监控
复杂点在于:binlog 方案要处理 DDL 变更告警、主键变更的 key 映射;轮询方案要防重复消费和位点丢失。但这两者都比在触发器里硬塞 UDF 更接近生产可用。











