自治事务触发器脱离主事务,独立提交导致数据不一致;仅适用于审计日志,禁用于计数、校验等场景;应移出触发器,改用应用层事务或异步方案。

自治事务触发器不参与主事务,天然破坏一致性
PL/SQL 中加了 PRAGMA AUTONOMOUS_TRANSACTION 的触发器,会脱离当前事务上下文,开启一个完全独立的事务。这意味着:主事务回滚时,自治事务已提交的 INSERT、UPDATE、日志写入等操作全部保留——形成“孤立项”。你看到的“成功记录日志”,实际和业务数据处于分裂状态。
- 主事务因下游失败而回滚 → 订单表没插入 → 但自治触发器已把日志写进
audit_log表并提交 - 主事务中修改了
user.balance,自治事务里查SELECT balance FROM user WHERE id = :NEW.user_id→ 查不到未提交值(READ COMMITTED 隔离),结果为 NULL 或旧值,导致校验误判 - 自治事务里再调用含触发器的表(比如写
counter表),会重新进入触发链,但此时主事务的:NEW/:OLD不可用,直接报ORA-04091
自治事务不是“异步”或“后台”,而是“另起炉灶”
很多人误以为自治事务只是“不阻塞主流程”,其实它连事务边界都变了。它有自己的 SCN、自己的锁、自己的提交点。在多行 DML(如 INSERT INTO t SELECT ...)下,每个行级自治触发器都开一个新事务,1000 行就可能产生 1000 个独立提交——极易引发死锁,尤其当多个自治事务同时尝试 SELECT FOR UPDATE 同一批关联资源时。
- 不要在自治事务里读主事务正在修改的表(哪怕只读),隔离级别决定了你看不见
:NEW值 - 避免嵌套调用:自治事务中调用的存储过程若也含触发器,会触发二次
ORA-04091 - 审计日志是唯一被 Oracle 官方认可的合理用途;用它做计数器、状态同步、跨表校验,全是高危操作
替代方案比“绕过错误”更重要
真正需要的是把逻辑移出触发器。比如原本想在 AFTER UPDATE 里更新统计表,应改为:应用层在同一个事务中执行 UPDATE + UPDATE summary_table;或改用 tx_outbox 表 + 异步消费者,由外部服务保证最终一致。
- 触发器只保留最轻量行为:修正
:NEW字段(BEFORE)、设默认时间戳、简单非空校验 - 所有涉及多表、远程、聚合、I/O 的逻辑,必须交还应用层或独立服务
- 如果已有自治事务触发器在线上运行,务必检查其写入目标是否被主事务回滚后仍残留——这是数据不一致最隐蔽的来源
自治事务的“独立性”是一把双刃剑:它解决了 ORA-04091 的报错,却把一致性问题从“立刻失败”变成了“静默分裂”。修复成本远高于预防。

















