触发器本质是同步阻塞的,不可真正异步;正确做法是触发器仅写轻量标记或发通知,由外部进程处理后续逻辑。

因为触发器根本不是异步的——它强制同步执行,且必须等任务完成才能提交事务。 你想让它“异步”,但它连“暂停”都不允许。
触发器里调用外部服务等于把网络延迟塞进数据库事务
常见错误现象:INSERT 一条订单,触发器里执行 sp_OACreate 调用 HTTP 接口,结果整个插入卡住 3 秒以上;应用报 Timeout expired,但数据库慢日志里只看到一条普通 INSERT。
- SQL Server / MySQL / PostgreSQL 的触发器全部运行在主事务上下文中,
AFTER INSERT不返回,事务就无法提交,行锁、页锁、间隙锁全挂着不动 - 哪怕只是
curl或http_request()这类封装函数,本质仍是同步阻塞调用,DNS 解析、TCP 握手、TLS 握手、服务端响应延迟,全算在事务耗时里 - 超时不是“任务失败”,而是“事务被杀”——
Lock wait timeout exceeded或Transaction was deadlocked是表象,根子在触发器没跑完
所谓“异步封装”在触发器里基本不可行
有人试图用 WAITFOR DELAY、dblink_send_query 或自建线程模拟异步,结果更糟。
-
WAITFOR DELAY不是异步,是主动挂起事务,锁照样持有,其他连接照常被堵 - PostgreSQL 的
dblink默认同步模式,dblink_exec返回前事务不释放远端连接,还可能造成主库回滚而远端已写入的“数据不一致” - MySQL 没有原生异步机制,
INSERT DELAYED已废弃,EVENT不响应 DML,BLACKHOLE表不能承载逻辑
真正能落地的替代方案只有两种
核心原则:触发器只落轻量标记,其余交给外部进程——不是“怎么异步”,而是“根本不让它同步”。
- MySQL:触发器只写
INSERT INTO trigger_queue (table_name, row_id, event_type) VALUES ('orders', NEW.id, 'INSERT'),别查、别算、别调函数;消费者用SELECT ... FOR UPDATE SKIP LOCKED拉取,失败重试走死信表 - PostgreSQL:触发器内一行
PERFORM pg_notify('orders_inserted', NEW.id::text),worker 进程LISTEN orders_inserted后立即拉数据;注意NOTIFY不持久,worker 必须自带幂等和重试 - SQL Server:必须用
SERVICE BROKER,SEND ON CONVERSATION才安全;sp_send_dbmail看似异步,实则仍属事务一部分,发不出就回滚
最容易被忽略的一点:触发器里的“成功”,只代表那条 INSERT 语句没报错;它掩盖了下游通知失败、缓存未更新、日志漏写等所有最终一致性环节的问题。你看到的是数据库层面的干净,实际业务早已断链。











