必须用自治事务,否则日志写入会破坏主事务边界,导致业务数据提前提交或错误日志被回滚丢失;INSERT INTO log_table不能直接放在主过程EXCEPTION块中,因非自治DML属于主事务一部分,COMMIT会强制提交未完成业务,ROLLBACK则连日志一并清除。

必须用自治事务(PRAGMA AUTONOMOUS_TRANSACTION),否则日志写入会破坏主事务边界,导致业务数据提前提交或错误记录被回滚丢失。
为什么INSERT INTO log_table不能直接放在主过程的EXCEPTION块里
主事务未结束前,任何非自治的DML操作(包括日志插入)都属于该事务一部分。一旦你在EXCEPTION里执行INSERT后又COMMIT,整个调用链的事务就被强制提交——刚生成的订单还没校验完就落库了;如果后续ROLLBACK,连错误日志也一起消失,查无可查。
常见错误现象:
- 业务表刚
INSERT一行,日志INSERT + COMMIT后立刻可见,但主逻辑其实该回滚 -
RAISE抛出异常后,调用方执行ROLLBACK,审计表里空空如也 - 多个嵌套过程共用同一张日志表,却因事务耦合导致部分记录“半写入”状态
PROC_SAVE_ERRMSG该接收哪些参数才真正有用
只记“ORA-01403: no data found”毫无价值,必须捕获能定位到具体调用点和上下文的信息。参数设计不是越多越好,而是要确保每个字段在排障时可交叉验证。
-
biz_code:必须由调用方传入,比如订单号'ORD20260826001',不能靠SYS_CONTEXT('USERENV', 'SESSIONID')替代——它无法关联业务单据 -
errorline:用DBMS_UTILITY.format_error_backtrace,不是$$PLSQL_LINE;后者只返回当前EXCEPTION块行号,前者能穿透到最内层出错位置(比如包体第87行) -
errorcode:直接用SQLCODE,注意它是负值(如-1表示主键冲突),别做ABS()处理,否则和SQLERRM里的ORA-00001对不上 -
msg:用SQLERRM原值,它已含错误码前缀,重复拼接会导致ORA-00001: ORA-00001: unique constraint violated
自治事务日志过程里为什么不能调用DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT缓冲区绑定在会话级,而自治事务会启动一个独立会话上下文。你在自治事务里调用DBMS_OUTPUT.PUT_LINE,输出内容不会出现在主会话的DBMS_OUTPUT窗口中,调试时完全不可见;更严重的是,某些客户端(如SQL Developer)甚至会因此报ORA-20000: DBMS_OUTPUT buffer overflow,导致整个日志过程失败。
实操建议:
- 彻底移除自治事务过程内的
DBMS_OUTPUT调用,改用INSERT写入日志表并立即COMMIT - 若需临时调试,可在主过程的
EXCEPTION块中(自治事务调用前)加一句DBMS_OUTPUT.PUT_LINE(SQLERRM),仅作快速验证 - 生产环境禁用
DBMS_OUTPUT,它不持久、不可审计、无时间戳、无法关联会话
日志表结构和索引容易被忽略的关键点
一张没索引的日志表在高并发写入下会迅速成为瓶颈,而字段类型不匹配则导致隐式转换,让WHERE biz_code = 'xxx'走不了索引。
-
biz_code字段必须定义为VARCHAR2(100)或更长,且建普通B-Tree索引;用CHAR会导致尾部空格填充,查询时必须写RTRIM(biz_code) = 'xxx' -
created_at用TIMESTAMP WITH TIME ZONE,避免跨时区部署时日志时间错乱;同时建函数索引CREATE INDEX idx_log_time ON audit_log (TRUNC(created_at)),方便按天归档 - 避免在日志表上建触发器——自治事务已隔离,再加触发器只会引入额外开销和死锁风险
- 定期清理用
PARTITION BY RANGE (created_at),别依赖DELETE FROM audit_log WHERE created_at ,大表删除易锁表
自治事务本身不解决所有问题:它只保证日志写入独立,但不保证日志表可用、磁盘不爆、连接池不断。上线前务必压测日志写入路径,确认PROC_SAVE_ERRMSG在每秒百次调用下仍能稳定COMMIT成功。

















