审计日志表须按月分区并建(user_id,op_time)联合索引,禁用触发器而改用应用层写入,字段精简至必要项,归档采用RENAME+INSERT分步操作。

日志表必须带时间分区和索引,否则查 audit_log 会越来越慢
MySQL 的审计日志写入频繁、查询低频但要求范围精确(比如“查某用户上周所有 DELETE 操作”),不加约束很快就会卡死。核心是让 WHERE 条件能走索引,且避免全表扫描。
常见错误现象:SELECT * FROM audit_log WHERE user_id = 123 AND op_time BETWEEN '2024-06-01' AND '2024-06-07' 执行超时,EXPLAIN 显示 type=ALL。
- 主键用
(id BIGINT AUTO_INCREMENT),别用复合主键——插入性能差,且 MySQL 8.0+ 对复合主键的自增行为有隐式依赖 - 必建联合索引:
INDEX idx_user_time (user_id, op_time),顺序不能反:user_id是等值条件,op_time是范围,符合最左前缀原则 - 按月分区(非分表):
PARTITION BY RANGE (YEAR(op_time)*100 + MONTH(op_time)),配合ALTER TABLE ... REORGANIZE PARTITION预建下月分区,避免写入时触发自动分裂锁表 - 禁止在
op_time上用DATETIME存毫秒级时间——精度够用即可,TIMESTAMP节省 1 字节且自动时区转换,但要注意 MySQL 5.6 默认只到秒;如需毫秒,用DATETIME(3)并确保客户端和连接层都支持
归档表不能直接 TRUNCATE 或 DROP,要用 RENAME + INSERT DELAYED
线上审计表不能停写,归档本质是“把旧数据搬走,同时保证新写入不中断”。直接 TRUNCATE audit_log_202405 会锁表,DROP 更危险——万一误删或没备份就不可逆。
使用场景:每月 1 号凌晨归档上月数据到历史库,保留最近 3 个月在线可查。
- 归档动作分三步:先
CREATE TABLE audit_log_202405 LIKE audit_log,再INSERT INTO audit_log_202405 SELECT * FROM audit_log WHERE op_time ,最后 <code>DELETE FROM audit_log WHERE op_time -
INSERT ... SELECT前务必加SET SESSION sort_buffer_size = 4194304(4MB),否则大结果集排序可能用磁盘临时表,拖慢归档速度 - 删除后立即执行
OPTIMIZE TABLE audit_log——InnoDB 不会自动回收空间,碎片率高会导致后续插入变慢 - 归档表命名统一加后缀
_archive或年月,比如audit_log_archive_202405,避免和在线表名混淆
触发器写审计日志?别用,改用应用层显式 INSERT 或 MySQL 8.0+ 的 UNIFIED_LOG
用 BEFORE UPDATE 触发器往 audit_log 插数据,看似自动,实则埋雷:事务失败时审计记录可能残留、高并发下触发器锁竞争严重、无法记录客户端 IP 和执行语句原文。
解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。
错误现象:UPDATE users SET name='x' WHERE id=1 成功,但审计表里多出一条空 sql_text 记录;或者批量更新卡住,SHOW PROCESSLIST 显示大量 Waiting for table metadata lock。
- 应用层写审计更可控:在业务事务提交前,拼好
INSERT INTO audit_log (user_id, table_name, op_type, sql_text, client_ip, op_time) VALUES (...),和主 SQL 同事务提交 - MySQL 8.0.14+ 可启用
log_error_services = 'log_filter_internal; log_sink_internal; log_sink_syseventlog'配合performance_schema.audit_log表,但仅记录连接/权限类事件,不记录 DML 内容,不能替代业务审计 - 如果非要用触发器,必须限制字段长度:
sql_text VARCHAR(1024),避免超长语句导致行溢出,引发锁等待
audit_log 表字段设计要克制,别堆 JSON 或冗余字段
有人喜欢加 old_data JSON、new_data JSON、trace_id VARCHAR(64)、app_name VARCHAR(32)……结果单行超 8KB,InnoDB 页存不下,强制外存,查询变慢,备份体积翻倍。
真实需求其实是:谁、什么时候、在哪张表、做了什么操作、影响了哪些行(主键值)、是否成功。
- 只保留必要字段:
id,user_id,table_name,op_type ENUM('INSERT','UPDATE','DELETE'),pk_value VARCHAR(255)(存主键值,如'123'或'u123,o456'),op_time,client_ip,status TINYINT(1=成功,0=失败) - 不要存完整 SQL:
sql_text字段删掉——既难解析又占空间;真要追溯,靠user_id + op_time + pk_value关联业务日志更准 -
pk_value用VARCHAR而非TEXT:避免隐式转换,索引失效;复合主键场景用逗号分隔,不用 JSON,方便WHERE pk_value LIKE 'u123%'简单过滤 - 字符集统一用
utf8mb4,但排序规则用utf8mb4_0900_as_cs(大小写敏感),避免user_id大小写混用导致查不到记录
真正麻烦的是跨库操作和分布式事务下的主键一致性——比如订单库和库存库各自生成 ID,audit_log 里 pk_value 怎么对齐。这得靠业务层约定格式,数据库层面管不了。

















