AFTER触发器无法实现异步通知,因其同步执行会阻塞事务提交;真异步需用Service Broker“发送即忘”或轻量表队列+外部轮询,避免事务耦合与死锁。
AFTER触发器本质是同步执行的——它会阻塞事务提交,直到触发器逻辑全部完成。如果你在 AFTER INSERT 里直接调用 HTTP API、发消息到 RabbitMQ 或写入外部队列,不仅会让业务 SQL 响应变慢,还会因网络超时或下游故障导致整个事务回滚。这不是“异步”,而是把外部依赖拖进了事务边界,违背了队列解耦的初衷。
SQL Server 自带的 Service Broker 是唯一原生支持事务内“发送即忘”(fire-and-forget)的机制。它允许你在触发器中把消息写入队列,且该操作与当前事务原子性绑定:事务成功则消息入队,事务失败则消息自动丢弃。
CREATE QUEUE 和 CREATE SERVICE 必须提前建好,且数据库需启用 ENABLE_BROKER(用 ALTER DATABASE [db] SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE)SEND ON CONVERSATION,不解析消息内容、不等待响应XML 或 VARBINARY,避免字符集/长度问题;例如:CAST((SELECT inserted.id, inserted.status FOR XML RAW, TYPE) AS VARBINARY(MAX))
END CONVERSATION——留给外部激活存储过程处理,否则会中断会话生命周期如果无法启用 Service Broker(如某些云托管 SQL Server 实例禁用),可用“写表+轮询”模拟队列。关键是让触发器只做最轻量的插入,把重活交给外部服务。
id BIGINT IDENTITY、table_name SYSNAME、row_id INT(或对应主键类型)、created_at DATETIME2 DEFAULT GETDATE()、processed BIT DEFAULT 0
INSERT INTO notification_queue (table_name, row_id) VALUES ('orders', @inserted_id),禁止 JOIN、子查询、函数调用WHERE processed = 0 ORDER BY id 分批读取,处理完再 UPDATE ... SET processed = 1;注意加 WITH (READPAST) 避免锁阻塞WAITFOR DELAY 在 SQL 里轮询——这会浪费连接资源,交给应用层控制频率更可靠哪怕只是往通知表 INSERT 一行,若触发器和业务 SQL 同时高并发更新同一张主表,仍可能引发死锁。尤其当主表有非聚集索引、触发器又隐式扫描索引时。
inserted 或 deleted 的多行结果集做复杂逻辑;批量操作时,inserted 可能含数百行,SELECT ... FROM inserted 若没加 TOP 1 或明确限定,极易拖慢INSERT INTO orders SELECT * FROM staging_orders),而不是单行测试——这才是死锁高发点Service Broker 的会话状态管理、表队列的幂等消费、以及触发器对大事务的敏感度,才是落地时真正卡住进度的地方。