audit_log表应以audit_id为主键,同时必须创建复合索引idx_audit_table_record_id(table_name在前)和单独索引idx_audit_timestamp;避免在old_data/new_data上建索引,索引数量需严格按高频查询路径控制。

audit_log 表的主键和 record_id 索引怎么选
直接用 audit_id 当主键没问题,但仅靠它查日志效率极低——你几乎从不按自增ID查审计记录,而是按 table_name + record_id 追溯某条业务数据的全部变更,或按 change_timestamp 查某段时间的操作。所以必须补复合索引:CREATE INDEX idx_audit_table_record_id ON audit_log (table_name, record_id);。注意顺序:table_name 在前,因为查询时通常先固定表名(比如只查 users 表的变更),再筛具体记录。如果把 record_id 放前面,这个索引对「查某张表所有变更」就基本失效。
时间范围查询慢?加单独的 change_timestamp 索引
合规审计常要求导出「过去30天所有DELETE操作」或「某小时内的全部UPDATE」,这类查询走全表扫描非常伤。给 change_timestamp 单独建索引最直接:CREATE INDEX idx_audit_timestamp ON audit_log (change_timestamp);。别试图把它塞进复合索引里凑数——时间范围查询(BETWEEN、>=)在复合索引中只有当它是最左前缀时才高效,而你不可能每次查询都带上 table_name 和 record_id。
为什么不要在 old_data / new_data 上建索引
old_data 和 new_data 是 JSON 类型字段,MySQL 5.7+ 虽支持 JSON 列上建虚拟列索引,但实际收益极小:审计日志的检索逻辑极少基于「旧值包含某个字段」这种条件;即使有,也该由应用层解析后落库为明确字段(比如加 old_status、new_amount),再针对这些字段建索引。直接在 JSON 上建索引不仅无效,还会拖慢写入——每次 INSERT 都要解析并更新索引树。
高并发写入下,索引数量不是越多越好
- 每个索引都会增加
INSERT开销,audit_log 是写多读少的典型表,过多索引会明显拖慢触发器执行速度 - 优先保障
(table_name, record_id)和change_timestamp这两个最常用路径 - 如果业务需要按
changed_by查(比如「某个用户的所有操作」),再加一个单列索引:CREATE INDEX idx_audit_changed_by ON audit_log (changed_by);,但别提前加
真正容易被忽略的是索引维护成本——触发器每写一条审计日志,数据库就得同步更新所有相关索引。在日均百万级变更的系统里,多一个无用索引可能让整体写入延迟翻倍。











