AFTER触发器才能可靠记录审计日志,因其在变更提交后稳定获取完整NEW/OLD值;BEFORE易致事务回滚拖垮业务;需结构化存储、用USER()/CONNECTION_ID()溯源、严测批量与冲突场景。

触发器里用 AFTER INSERT/UPDATE/DELETE 才能记下真实变更
审计日志的核心是“变更发生后立刻捕获”,不是“准备变更时猜测”。BEFORE 触发器看到的是旧值或待写入值,NEW 和 OLD 在 INSERT 时 OLD 为空、DELETE 时 NEW 为空,只有 AFTER 能稳定拿到完整上下文。
常见错误是写成 BEFORE UPDATE 然后试图记录 OLD.column 和 NEW.column 的差异——这看似合理,但一旦触发器里抛错(比如日志表字段长度不够),整个事务会回滚,业务写入失败,审计没做成,主流程反而被拖垮。
-
AFTER INSERT:直接取NEW.*,无需比对 -
AFTER UPDATE:必须显式判断OLD.col != NEW.col,不能无脑全字段记录(否则日志爆炸) -
AFTER DELETE:只取OLD.*,注意大文本字段(如TEXT)可能超日志表限制
审计表字段设计要避开 JSON 和 LONGTEXT 坑
很多人图省事把整行变更塞进一个 JSON 字段,结果发现:MySQL 5.7 的 JSON 函数不支持索引,8.0 虽然支持但查询慢;更麻烦的是,JSON_OBJECT() 对 NULL、二进制、时间类型处理不一致,容易在触发器里报 Truncated incorrect DOUBLE value 这类隐晦错误。
推荐方案是固定结构:至少包含 table_name、operation(INSERT/UPDATE/DELETE)、pk_value(主键值,字符串化)、changed_fields(逗号分隔的字段名)、old_values 和 new_values(两个 VARCHAR(1024),存关键字段的变更快照)。这样查起来快,也方便后续按表、按字段筛选。
- 避免用
LONGTEXT存变更内容——它不参与排序和索引,且触发器执行时可能触发max_allowed_packet限制 -
created_at必须用NOW(3),别用CURRENT_TIMESTAMP,后者在某些复制场景下会变成从库时间 - 审计表引擎必须是
InnoDB,否则无法保证和主表事务一致性
触发器里调用 USER() 和 CONNECTION_ID() 才能定位真实操作人
应用层传过来的“操作人”不可信——可能是伪造的字段,也可能中间件重写了。MySQL 原生的 USER() 返回 'user@host' 格式,CONNECTION_ID() 是当前连接唯一 ID,两者结合才能追溯到具体会话。如果用了连接池(如 HikariCP),USER() 可能全是应用账号,这时得靠 information_schema.PROCESSLIST 关联查,但触发器里不能查这个表,所以必须提前把 APPLICATION_NAME 或 client_ip 通过 SET @app_user = 'xxx' 注入,并在触发器里读 @app_user。
- 不要依赖
CURRENT_USER()——它返回权限校验后的账号,和实际连接人可能不同 -
USER()中的 host 部分可能被 DNS 缓存污染,生产环境建议配合@@hostname一起记 - 如果应用用了代理(如 ProxySQL),
USER()的 host 会变成代理地址,需在代理层透传真实 IP 到自定义变量
上线前必须测 INSERT ... ON DUPLICATE KEY UPDATE 和批量操作
审计触发器最常翻车的不是单条语句,而是批量写入和冲突更新。比如 INSERT INTO t VALUES (...), (...) ON DUPLICATE KEY UPDATE x=VALUES(x),这种语句会为每行触发一次 AFTER INSERT 和一次 AFTER UPDATE(如果冲突),但 OLD 和 NEW 的值可能不符合直觉——特别是当 ON DUPLICATE KEY 更新了非主键字段时,OLD 是冲突前的值,NEW 是 VALUES() 提供的值,不是最终落库值。
- 用
LOAD DATA INFILE导入数据时,触发器默认不生效(除非显式开启sql_log_bin=ON且 binlog_format=ROW) - 批量
UPDATE如果没带WHERE条件,会为每一行都触发,日志量可能瞬间打爆磁盘 - 测试必须覆盖
REPLACE INTO场景——它本质是DELETE + INSERT,会触发两次审计,但OLD和NEW的主键值相同,容易误判为重复日志
真正难的不是写触发器,是让日志既不丢、不错、不拖慢业务,又能在出问题时快速反查。字段变更判断、连接上下文捕获、批量语义兼容——这三块漏掉任何一块,审计就只剩形式主义。










