sql server触发器+service broker可实现本地事务内准实时解耦,触发器仅写入本地队列,激活存储过程异步转发;mysql则必须通过binlog解析(如debezium)实现跨平台同步,不可在触发器中调用网络操作。

SQL Server 触发器 + Service Broker 能做到本地实时解耦
SQL Server 原生支持在触发器里发消息到 Service Broker 队列,这是唯一不依赖外部中间件、事务内可控的“准实时”同步出口。它不跨平台,但能把你从阻塞式远程写入中解放出来。
关键点在于:触发器只负责把变更写进本地队列(INSERT INTO DataSyncQueue),后续由激活存储过程异步消费,再调用 HTTP / Kafka / RabbitMQ 客户端转发——这样即使下游失败,也不卡主库 DML。
- 必须启用
Service Broker:ALTER DATABASE YourDB SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE - 触发器里不能直接调用
SEND,得先DECLARE @h UNIQUEIDENTIFIER; BEGIN DIALOG @h ...; SEND ON CONVERSATION @h ... - 激活过程要用
WAITFOR (RECEIVE ...)循环读取,且需处理END CONVERSATION和错误会话 - 消息体建议用 JSON:
SELECT 'INSERT' AS op, * FROM inserted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
MySQL 触发器无法直连消息队列,必须绕过 binlog
MySQL 触发器里不能执行 SYS_EXEC()、HTTP_CLIENT() 或任何网络调用——这些函数要么被禁用,要么根本不存在。硬写会报错 ERROR 1371 (HY000): Cannot modify a table in a stored function/trigger 或权限拒绝。
真正可行的路径是放弃触发器,改用 binlog 解析:
- 开启
binlog_format = ROW和binlog_row_image = FULL,否则字段缺失 - 用
Debezium监听 binlog,输出变更事件到Kafka;或用Maxwell直接 stdout 输出 JSON - 消费者服务收到事件后,再写目标平台(PostgreSQL / Elasticsearch / Redis)
- 如果目标平台是 HTTP 接口,建议加一层幂等 key(如
db_table_pk_ts),避免重复投递
跨平台同步时,触发器里的字段映射最容易出错
哪怕只是 MySQL → PostgreSQL 同步,字段类型、默认值、空值处理也常导致失败。比如 MySQL 的 TINYINT(1) 被当布尔,PostgreSQL 却要 BOOLEAN;MySQL 的 DATETIME 精度是微秒,PostgreSQL 默认只到毫秒。
不要指望触发器自动转换:
- 别在触发器里写
CAST(inserted.status AS BIT)这种硬转,目标库类型一变就崩 - 用中间格式统一:所有字段转成字符串或 JSON,交由下游解析(例如用
JSON_OBJECT('id', id, 'created_at', UNIX_TIMESTAMP(created_at))) - 时间字段务必显式转为 UTC:
CONVERT_TZ(created_at, @@session.time_zone, '+00:00') - 对
NULL和空字符串做区分处理,尤其在目标库有NOT NULL约束时
别让触发器承担“最终一致性”的责任
触发器本质是强耦合、同步执行的机制,而跨平台同步天然需要重试、死信、延迟补偿。一旦你在触发器里嵌入 HTTP 调用或链接服务器写入,就等于把网络抖动、下游超时、序列化失败全部拖进主事务里。
真实系统里最常被忽略的是:触发器成功 ≠ 同步成功。你看到 INSERT INTO orders 返回了,但消息可能卡在队列里、HTTP 请求早已超时、Kafka 分区已满。
所以必须分层设计:
- 触发器只管“记录变更”,写本地日志表或 Service Broker 队列
- 独立 worker 进程拉取变更,带指数退避重试,失败进
dead_letter_task表 - 监控项不是“触发器是否执行”,而是“
dead_letter_task表是否有堆积”
跨平台同步的复杂点不在语法,而在故障传播路径不可控——你永远不知道是网络断了、目标库锁表了,还是对方 API 改了返回结构。触发器不适合当第一道防线。










