general_log不能替代审计日志——它无结构、无身份上下文、不记录执行结果且性能损耗大;虽能捕获GRANT/REVOKE,但因默认关闭、输出方式限制、云环境屏蔽及日志轮转等原因常查不到,仅适合低峰期临时排查。

通用查询日志(general_log)能抓到 GRANT/REVOKE,但不能当审计日志用——它没结构、没身份上下文、不记录执行结果,且开启即伤性能。
为什么 general_log 查不到权限变更记录
不是语句没记,而是你查的位置或方式错了。常见失效原因:
-
general_log默认是 OFF,MySQL 8.0+ 启动后完全不写,SET GLOBAL general_log = ON只在当前会话生效,服务重启就丢 -
log_output是'TABLE'时,日志写入mysql.general_log表,但该表默认引擎为 CSV、无索引、且 MySQL 8.0+ 默认禁用——SELECT返回空也不报错 - 云数据库(如阿里云 RDS)通常屏蔽
SET GLOBAL general_log,只能通过控制台开启,且日志仅保留 7 天,过期自动清空 - Linux 下
logrotate每天轮转日志,真实文件可能是general.log-20260507,不是general.log
如何临时启用并安全提取 GRANT/REVOKE 语句
仅限低峰期、小时级排查,别上生产主库。操作前确认你有 SUPER 权限:
- 查当前配置:
SELECT @@log_output, @@general_log_file, @@general_log; - 动态开启(不重启):
SET GLOBAL log_output = 'FILE'; SET GLOBAL general_log = ON; - 确认日志路径可写:
SELECT @@general_log_file;,然后ls -l $(dirname @@general_log_file)看 MySQL 进程是否有写权限 - 立刻验证是否生效:
tail -f $(SELECT @@general_log_file),再手动执行一条GRANT SELECT ON test.* TO 'u'@'%';,看是否实时出现 - 提取权限语句:
grep -iE "(grant|revoke|create user|drop user|alter user)" /var/lib/mysql/general.log
注意:日志里 user@host 字段是连接用户,不是实际执行者;失败的 GRANT(如用户不存在)也会记录,但不带 ERROR 1396 这类状态码。
用 TABLE 模式查 mysql.general_log 的坑
哪怕你设了 log_output = 'TABLE',也别指望靠它做审计:
-
mysql.general_log是 CSV 引擎,SELECT ... LIKE 'GRANT %'全表扫描,10 万行就卡住 -
argument字段长度受max_allowed_packet限制,长语句会被截断,GRANT ... ON huge_db.* TO ... IDENTIFIED BY ... REQUIRE X509可能只留半截 - 字段无时间索引,
ORDER BY event_time DESC LIMIT 100在大数据量下极慢 - MySQL 8.0+ 默认禁用该表,即使开了
general_log,SELECT仍返回空——得先ALTER TABLE mysql.general_log ENGINE=CSV;(不推荐)
真正该盯住的三个盲区
就算 general_log 开着,这三类权限变更你也看不到:
-
FLUSH PRIVILEGES:它只是重载内存,不触发 SQL 执行事件,audit_log和server_audit都不记 - 直连改系统表:
UPDATE mysql.user SET Select_priv='Y' WHERE User='x';虽然会被 binlog 或 audit 记录,但语义模糊,看不出是“授 SELECT”还是“误开后门” - 中间件代执行:ProxySQL、MaxScale 或应用层连接池发来的
GRANT,日志里user@host显示的是中间件地址,不是原始操作人 IP
想可靠审计,社区版必须用 server_audit 插件并明确开启 QUERY_DDL;general_log 只适合关机前最后一小时的手动快照,别把它当长期方案。


















