历史表和审计日志表应该怎么设计?
简化版
历史表记录业务对象随时间变化的版本,审计日志记录谁在什么时候做了什么操作。历史表偏业务状态追溯,审计表偏操作追责和安全合规;设计时要记录主键、版本、变更前后值、操作人、操作时间、来源和请求 ID。
详细版
历史表常见字段:
id
biz_id
version
valid_from
valid_to
snapshot_json
created_at
审计日志常见字段:
id
actor_id
action
resource_type
resource_id
before_data
after_data
request_id
ip
created_at
设计要点:
- 历史表要能还原某个时间点的数据状态;
- 审计表要能回答谁、何时、从哪里、对什么做了什么;
- 日志表通常只追加不更新;
- 大量日志要分区、归档或进入日志系统;
- 敏感字段要脱敏或加密。
完整版教学
一、历史表和审计表目标不同
历史表回答的是:这个业务对象过去是什么状态?
审计表回答的是:谁对这个对象做了什么操作?
比如商品价格从 100 改到 80,历史表关心价格版本;审计表关心哪个管理员、通过哪个后台页面、在什么时间做了修改。
二、历史表要能还原状态
历史表常见两种方式:
- 快照式:每次变更保存整份对象快照;
- 差异式:只保存变更字段和前后值。
快照式恢复简单,但占空间;差异式节省空间,但恢复某个时间点状态需要从多个变更合并。
业务重要、字段不太大的对象,快照式更稳;变更频繁且对象很大时,可以考虑差异式或混合方式。
三、版本和有效期很关键
历史表最好有版本号或有效期:
version
valid_from
valid_to
这样可以回答“2026 年 7 月 1 日这份合同条款是什么”。如果只有一堆更新时间,没有有效期模型,回溯查询会很痛苦。
四、审计日志要保留操作上下文
审计日志不只是保存变更内容,还要保存上下文:
- 操作人;
- 操作类型;
- 资源类型和资源 ID;
- 请求 ID;
- IP 和设备信息;
- 操作入口;
- 变更前后值;
- 操作时间。
请求 ID 特别有用,它能把应用日志、网关日志、数据库审计串起来。
五、审计日志通常只追加
审计日志的可信度来自不可随意修改。通常设计成只追加,不更新,不物理删除,或者删除也要有更高权限和留痕。
如果审计日志也能被普通业务随便改,那它就失去了追责价值。
六、数据量和敏感信息要提前考虑
审计和历史数据增长很快。设计时要考虑:
- 按时间分区或分表;
- 定期归档;
- 冷热存储;
- 查询索引;
- 敏感字段脱敏;
- 合规保留期限。
比如 before_data 和 after_data 里不能随便明文存身份证、银行卡、密码哈希等敏感信息。
还有一个容易被忽略的点是索引数量。审计表常见查询是按 resource_type + resource_id 查某个对象的操作轨迹,或按 actor_id + created_at 查某个人的操作记录;不要给 JSON 大字段随意建索引。数据量达到千万级后,过多索引会让追加写入、归档和分区维护都变重。
七、常见误区与追问
| 设计目标 | 历史表 | 审计日志表 |
|---|---|---|
| 核心问题 | 某个时间点对象是什么状态 | 谁在什么时候做了什么 |
| 常见粒度 | 业务对象版本 | 一次操作或一次请求 |
| 查询方式 | 按 biz_id + version/valid_time 回溯 | 按操作人、资源、请求 ID 追踪 |
易错点:历史表和审计表可以同时存在。历史表偏“状态复原”,审计表偏“行为追责”,不要用一张流水表硬扛所有问题。
举个数字例子:一个商品每天改价 3 次,保存 365 天历史,快照式历史表大约是 3 * 365 = 1095 条版本记录;如果每份快照 2KB,大约 2MB 多,换来任意时间点恢复很划算。但如果对象快照是 200KB 且每分钟变更,差异式或字段级变更日志就更合适。
- 误区:有了审计日志就不需要历史表。 审计日志能说明操作过程,但未必方便恢复某个时间点的完整业务状态。
- 误区:历史表只存变更后的值就够了。 只存后值会让对比、回滚和责任判断变困难,关键字段通常要能看到前后变化。
- 误区:审计日志可以随业务记录一起更新。 审计日志的可信度来自追加和留痕,随意更新会削弱追责价值。
- 追问:快照式和差异式怎么选? 字段少、恢复要求高选快照式;对象大、变更频繁选差异式或混合式。
- 追问:为什么要存 request_id? 它能把审计表、应用日志、网关日志、链路追踪串起来,定位一次操作的完整上下文。
- 追问:敏感字段如何处理? 密码、证件号、银行卡等不要明文进审计日志,通常要脱敏、加密、哈希或只记录字段发生过变化。
八、加强记忆
历史表管“状态怎么变”,审计表管“谁做了什么”。历史表要有版本和有效期,审计表要有操作人、资源、前后值、请求 ID 和时间。两者通常只追加,并且要考虑归档、分区和敏感字段保护。