MySQL社区版必须启用server_audit插件并设置QUERY_DDL事件才能可靠审计权限变更,其他方案(audit_log、general_log、binlog)均存在缺失、不可靠或代价过高的问题。

MySQL 社区版默认不记录任何权限变更历史,GRANT、REVOKE、CREATE USER 等操作不会出现在 error log、slow log 或 binlog 中——想查“谁在什么时候改了什么”,必须主动启用审计插件,且不能依赖 general_log。
用 server_audit 插件捕获 QUERY_DDL 事件(社区版唯一可靠方案)
这是目前 MySQL 社区版最轻量、最稳定、唯一能精准捕获权限语句的方案。它专为 DDL 审计设计,性能影响小,日志结构清晰,且只记录真正相关的语句。
-
QUERY_DML不管用:它只抓INSERT/UPDATE/DELETE,对权限语句完全无效 - 必须显式设置:
SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL';——ALL会混入大量无关连接日志,增加解析负担 - 日志默认写入
/var/log/mysql/audit.log,但务必显式配置server_audit_file_path并确保目录存在、属主为mysql、有写权限 - 安装只需三句,无需重启:
INSTALL PLUGIN server_audit SONAME 'server_audit.so';,然后设事件和开关 - 确认插件路径:
SHOW VARIABLES LIKE 'plugin_dir';,再检查对应目录下是否存在server_audit.so(常见路径:/usr/lib/mysql/plugin/server_audit.so)
audit_log 插件在社区版基本不可用
别在社区版上浪费时间尝试 audit_log。它不是“没配好”,而是根本不存在。
- 执行
INSTALL PLUGIN audit_log SONAME 'audit_log.so';会报错:Plugin 'audit_log' is not loaded -
SHOW PLUGINS;查不到该插件;plugin_dir下大概率找不到audit_log.so - 即使找到第三方编译的版本,也常因 MySQL 版本号(如 8.0.34 vs 8.0.33)或 ABI 不匹配而加载失败
-
audit_log_policy = ALL在社区版完全无效——参数存在,但插件没加载,设了也没用 - 误判已启用的典型表现:日志文件始终为空,
tail -f无任何输出
别把 general_log 当审计工具
它确实能临时抓到 GRANT 和 REVOKE 的原始文本,但本质是调试日志,不是审计日志。
- 开启后 QPS 下降 15–30%,日志体积每天 GB 级,磁盘 I/O 成瓶颈
- 日志无结构:所有语句混在一起,
GRANT和SELECT COUNT(*)同一行,无法自动提取或告警 - 不分离真实执行者:
user@host字段无法区分代理账号、中间件或连接池代发行为 - 不记录执行结果:
GRANT ON nonexistent_db.*失败了?general_log照样记,但没标error_code,你得再翻错误日志对齐时间戳 - 若用
TABLE模式,mysql.general_log是 CSV 引擎,不支持索引,大数据量时LIKE查询极慢
binlog 解析仅作补充,不可依赖
只有在明确开启且保留足够久的 binlog 前提下,才可能回溯部分权限变更,但覆盖不全、解析成本高。
-
STATEMENT或MIXED格式下,GRANT语句会以原始 SQL 形式写入;ROW格式下只记录对mysql.user表的行变更,需人工解读语义 -
FLUSH PRIVILEGES永远不会出现在 binlog 中——它不走 SQL 执行路径,只是内存重载 - 直接
UPDATE mysql.user这类操作虽会被 binlog 记录,但日志里只看到 DML,看不出是否真影响权限逻辑 - 解析命令示例:
mysqlbinlog /var/lib/mysql/mysql-bin.000001 | grep -i "grant\|revoke",但需有文件系统读取权限 - binlog 不记录执行者 IP、主机名、客户端信息,无法定位具体操作来源
真正要落地权限变更审计,核心就一条:社区版必须用 server_audit + QUERY_DDL,其他路径要么缺失、要么不可靠、要么代价过高。最容易被忽略的是——插件加载成功后,必须手动设置事件类型,否则日志里照样看不到一条 GRANT。


















