MySQL 自带的 general_log 能不能直接用?
能,但不推荐在生产环境长期开启。它会记录所有语句(包括
SELECT),I/O 压力大、日志体积爆炸、还可能暴露敏感数据。
开启方式:
SET GLOBAL general_log = ON;,日志默认写入文件(路径由
general_log_file控制)或表
mysql.general_log(需设
log_output = 'TABLE')。但表模式下查起来慢,且没有索引优化,
SELECT * FROM mysql.general_log扫全表很卡。 只建议临时排障时开 1–2 分钟,立刻关掉:
SET GLOBAL general_log = OFF;日志表无主键、无时间索引,按需手动加:
ALTER TABLE mysql.general_log ADD INDEX idx_event_time (event_time);(但 MySQL 8.0+ 对该表结构有限制,部分字段不可修改) 文件模式下,日志无法按用户/数据库过滤,得靠外部脚本 grep,不灵活
用触发器 + 自定义日志表实现轻量级操作审计
适合记录关键表的增删改(比如用户表、订单表),可控、低侵入、可定制字段。
先建日志表:
CREATE TABLE user_action_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
table_name VARCHAR(64) NOT NULL,
action_type ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
pk_value VARCHAR(255) NOT NULL, -- 主键值,字符串兼容 INT/UUID
old_data JSON,
new_data JSON,
operator_user VARCHAR(64),
ip_address VARCHAR(45),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
触发器里用 USER()和
@@hostname获取操作者信息,但注意:如果是应用连接池统一账号,
USER()拿不到真实业务用户,得靠应用层传参写入
operator_user
old_data和
new_data用
JSON_OBJECT()构造,例如
JSON_OBJECT('name', OLD.name, 'email', OLD.email)
避免在高频写入表上加触发器,否则性能明显下降;单次触发器逻辑别做 HTTP 请求或复杂计算
用 MySQL 8.0+ 的 audit log plugin(企业版)还是社区版替代方案?
MySQL 社区版不带官方 audit plugin(那是企业版功能),但可以用 Percona Server 或 MariaDB 的开源审计插件,或者用
mysqlbinlog解析 binlog 做事后分析。
mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000001可读取行格式 binlog,看到具体变更数据,但它是二进制日志,不是“谁在什么时间做了什么”的语义日志 binlog 不记录
SELECT、不记录失败语句、不包含客户端 IP,且开启
binlog_format = ROW后体积增大,对主从延迟有影响 如果真要审计级日志,建议在应用层埋点:ORM 拦截 SQL 执行前后,把
user_id、
request_id、
sql、
params、耗时、结果状态打到 ELK 或本地文件,比 DB 层更准、更可控
日志保留与清理策略怎么定?
没自动清理机制的日志表,半年后就成性能黑洞。别指望 DBA 手动删。
用分区表按天/月切分:ALTER TABLE user_action_log PARTITION BY RANGE (TO_DAYS(created_at)) (...),删除旧分区比
DELETE快得多 用事件调度器定期清理:
CREATE EVENT cleanup_logs ON SCHEDULE EVERY 1 WEEK DO DELETE FROM user_action_log WHERE created_at注意:
DELETE大表会锁表(尤其 MyISAM),InnoDB 虽支持行锁,但大量删除仍引发碎片和事务日志暴涨,优先选
TRUNCATE PARTITION或归档后删表
真正难的不是记日志,是定义清楚“哪些操作必须留痕”“保留多久合规”“谁有权查”,技术只是执行环节。字段设计漏了
tenant_id或
app_version,后期补成本极高。
