SQL触发器不能安全可靠实现自动排队,因其共享事务导致并发冲突、阻塞下游且缺乏重试监控等能力;应改用Service Broker异步解耦处理。不行。SQL 触发器本身不能安全、可靠地实现「自动排队」逻辑。触发器运行在事务上下文中,所有操作与原始 DML 语句共享同一事务、同一锁粒度、同一执行线程。一旦你在
AFTER INSERT 触发器里尝试用 SELECT ... FOR UPDATE 去争抢队列位置,或用 UPDATE queue_table SET status = 'processing' WHERE id = (SELECT MIN(id) FROM queue_table WHERE status = 'pending') 这类逻辑,就会立刻暴露三个硬伤:— 触发器内无法控制并发调度顺序,SELECT MIN(id) 在高并发下大概率返回相同 ID,导致多个事务同时“抢到”同一个队列项;
— 所有排队相关操作被绑在用户事务里:只要前端事务不提交,队列状态就不可见,下游消费者无法感知,整个排队链路阻塞;
— 没有超时、重试、死信、积压监控等排队系统必备能力,出错即卡死,且错误堆栈只报在触发器内,难以定位是业务写入失败还是排队逻辑崩了。
INSERT INTO orders 和「把它塞进队列」不是原子的两个动作——前者成功后者失败,会导致数据不一致;而强行把二者塞进一个事务,又让订单写入响应时间直接受排队逻辑拖累,违背排队「解耦、异步、削峰」的本意。Service Broker 是唯一合规路径:AFTER INSERT 触发器里,只做一件事:SEND 一条消息到 Service Broker 队列,内容为新订单 ID 和必要上下文;ACTIVATION 绑定)从队列取消息,再执行真正的排队逻辑(比如插入 queue_items 表、调用外部服务、发通知);END CONVERSATION,消息天然具备持久化、顺序性、去重能力。— 别在触发器里写 WHILE 循环或递归调用试图“等位”;
— 别用 GETDATE() 或 NEWID() 当排队序号,它们不保证全局单调;
— 别把队列状态字段(如 status)和业务主表放一起,更新冲突概率极高;
— 如果用轮询式消费,务必加 WITH (READPAST) 和 TOP (1),否则容易锁表。