mysql 8.0社区版默认不包含audit_log.so插件,show plugins和select @@have_audit_log均返回空或no,此时install plugin必失败;需改用percona server或mariadb等支持审计的分支。

MySQL 8.0 社区版默认不带可用的 audit_log.so 插件文件,直接执行 INSTALL PLUGIN audit_log SONAME 'audit_log.so' 几乎必然报错 ERROR 1126 (HY000): Can't open shared library 'audit_log.so'——不是命令写错了,是文件根本不存在。
确认 audit_log 插件是否真正可用
别跳过这步。很多问题根源就在这里:社区版 MySQL 8.0.x 的官方二进制包(如 mysql-server-8.0.x.rpm 或 .deb)压根没打包 audit_log.so,SHOW PLUGINS LIKE 'audit_log' 返回空,SELECT @@have_audit_log 返回 NO,都说明你当前环境不具备启用条件。
必须先运行:
SELECT VERSION();
再运行:
SHOW VARIABLES LIKE 'have_audit_log';
如果返回 NO,以下操作全无效:
- 别试
INSTALL PLUGIN audit_log SONAME 'audit_log.so'—— 肯定失败 - 别在 my.cnf 里硬加
plugin_load_add = audit_log.so—— 启动会警告并跳过 - 企业版用户可继续;社区版用户请立刻转向 Percona Server 或 MariaDB 的
server_audit
audit_log_file 路径必须启动前配置且目录就绪
audit_log_file 是只读变量,运行时用 SET GLOBAL 修改完全无效。它只在 MySQL 启动时从配置文件读取一次,且对路径有硬性要求:父目录必须存在、属主为 mysql、权限允许写入。
典型错误现象:SHOW VARIABLES LIKE 'audit_log_file' 返回空值,或日志文件始终为空,MySQL 错误日志里却没有任何提示。
正确做法:
- 在
/etc/my.cnf的[mysqld]段添加:audit_log_file = /var/log/mysql/audit.log - 手动创建目录:
sudo mkdir -p /var/log/mysql - 修正属主:
sudo chown mysql:adm /var/log/mysql(注意不是root) - 确认 SELinux/AppArmor 不拦截:
ls -Z /var/log/mysql,CentOS/RHEL 上需sudo semanage fcontext -a -t mysqld_log_t '/var/log/mysql(/.*)?'并restorecon -Rv /var/log/mysql
audit_log_policy = ALL 实际漏掉大量关键操作
audit_log_policy = 'ALL' 听起来很全,但它只捕获通过标准 MySQL 协议建立的连接和语句事件,对三类高频场景完全静默:
- mysqldump、mysqlpump 等客户端工具的导出行为 —— 它们走的是协议层,但插件默认过滤掉了这类“工具连接”
- 存储过程中动态拼接执行的 SQL(
PREPARE/EXECUTE)—— 只记录 PREPARE 本身,不记录 EXECUTE 执行的语句内容 - 复制线程(IO_THREAD / SQL_THREAD)产生的内部查询 —— 包括 GTID 更新、relay log 读取等,均不审计
如果你需要覆盖这些,必须配合其他手段:
- 用
audit_log_events = 'CONNECT,QUERY,TABLE,QUERY_DCL'补充事件类型(仍无法覆盖 dump) - 对备份任务单独部署网络层抓包(如 tcpdump + pt-query-digest)
- 在应用层强制所有 DML 经过统一代理(如 ProxySQL),并在代理侧埋点
JSON 格式日志不能直接用 jq 解析整文件
audit_log_format = JSON 是推荐选项,但它生成的是“JSON Lines”格式:每行一个合法 JSON 对象,**没有外层数组,无逗号分隔**。直接 cat audit.log | jq '.' 会报错 parse error: Invalid numeric literal。
正确解析方式(示例用 Python):
import json<br>with open('/var/log/mysql/audit.log') as f:<br> for line in f:<br> if line.strip():<br> try:<br> event = json.loads(line)<br> print(event['name'], event['status'], event['user']['name'])<br> except json.JSONDecodeError:<br> pass
常见陷阱:
- 别设
audit_log_format = OLD—— 已废弃,字段缺失严重,连user和host都不记录 -
audit_log_buffer_size默认 1MB,QPS 高时缓冲区满会导致连接阻塞(非丢日志),建议设为4194304(4MB) - 日志轮转必须靠外部工具(logrotate),MySQL 自身不支持按时间切分;若用
audit_log_rotate_on_size,务必配audit_log_rotations,否则旧文件堆积不清理
最常被忽略的一点:audit_log 插件不记录任何 SQL 执行结果、不记录被修改的行数据、不加密日志内容——这意味着 audit.log 文件本身就成了高敏感资产,必须和数据库主数据一样严格管控访问权限与传输通道。











