audit_log表基础字段需覆盖“谁、何时、何资源、何操作”四维度:id、user_id、ip、action、resource_type、resource_id、before_data、after_data、created_at;事务处理按业务敏感度区分;分区+索引优化查询;mysql插件不可替代业务层审计。

audit_log 表必须包含哪些字段才够用
基础审计日志不是记录越全越好,而是要覆盖「谁、在什么时间、对哪个资源、做了什么操作」这四个核心维度,同时兼顾查询效率和存储成本。
推荐的最小字段集如下:
-
id:BIGINT UNSIGNED AUTO_INCREMENT,主键,避免 UUID 影响写入性能 -
user_id:BIGINT 或 VARCHAR(64),记录操作人标识(不建议只存 username,因可能重名或变更) -
ip:VARCHAR(45),支持 IPv6,不要用 INT 存 IPv4 -
action:ENUM('create','update','delete','login','logout') 或 VARCHAR(20),避免用自由文本,便于后续统计 -
resource_type:VARCHAR(32),如 'user'、'order'、'config',用于区分操作对象类型 -
resource_id:VARCHAR(64),统一用字符串存 ID,兼容 UUID、Snowflake、数字型主键 -
before_data和after_data:JSON 类型(MySQL 5.7+),仅在关键业务变更时记录差异字段,非必填;避免全量 dump 整行 -
created_at:DATETIME(3) 或 TIMESTAMP(3),带毫秒,用服务器时区统一写入(如 UTC),别依赖客户端时间
INSERT 审计日志时要不要用事务包裹业务操作
要,但必须分情况——不是所有审计都值得加事务,否则会拖慢高频写入场景(如登录日志)。
原则是:**业务一致性要求高的操作,审计必须与主业务同事务;低敏感、高吞吐的操作,可异步落库或降级为写入消息队列。**
- 用户资料修改、资金转账类操作:审计日志
INSERT必须和业务UPDATE在同一个事务中,否则出现「改成功了但没记日志」就是合规风险 - 登录/登出、页面访问类日志:可单独连接插入,甚至用
INSERT DELAYED(MySQL 8.0 已移除)或写入 Redis 后批量刷库,避免阻塞主流程 - 如果用 ORM(如 MyBatis、Sequelize),注意
@Transactional是否实际传播到审计日志 DAO 层;Spring 默认REQUIRED是 OK 的,但自定义连接或多数据源容易漏掉
如何避免 audit_log 表膨胀导致查询变慢
审计表不归档,半年后就查不动,这是最常被忽视的运维债。
关键不是「能不能删」,而是「怎么删得安全、查得快、不锁表」:
- 按月分区:
PARTITION BY RANGE (TO_DAYS(created_at)),配合DROP PARTITION快速清理旧数据,比DELETE WHERE快一个数量级且不锁全表 - 只在
created_at和resource_type上建复合索引,比如INDEX idx_type_time (resource_type, created_at),避免在before_data这种 JSON 字段上建索引(无效) - 禁止
SELECT *查审计表,尤其不要在应用层做分页LIMIT 10000,20—— 改用游标分页,基于created_at + id排序和条件过滤 - 如果审计量极大(日均百万+),考虑将冷数据迁出到 ClickHouse 或 S3+Presto,MySQL 只保留最近 90 天
MySQL 自带 audit_log 插件能不能替代业务层日志
不能,而且混用反而增加排查难度。
MySQL server 层的 audit_log 插件(如 Oracle 官方插件或 MariaDB 的 server_audit)记录的是连接、语句、结果集等底层行为,它不知道你的业务语义:
- 它无法告诉你「张三把订单 #123 的状态从『待支付』改成『已取消』」,只能记录一条
UPDATE orders SET status=... WHERE id=123 - 它默认不解析参数化查询中的值,
WHERE user_id = ?中的 ? 值不会出现在日志里 - 它无法关联你系统里的
tenant_id、app_version等上下文字段 - 开启后性能损耗明显(尤其高并发简单查询场景),且日志格式固定、难对接 SIEM 系统
真正该做的,是让业务代码在关键节点显式调用审计方法,把「意图」而不是「SQL」记下来。MySQL 插件只作为兜底或安全审计补充,比如监控异常连接或未授权 DROP TABLE。










