MySQL社区版唯一可行的DDL审计方案是server_audit插件,需安装后设置server_audit_events='CONNECT,QUERY_DDL'并启用server_audit_logging,日志结构化输出至指定路径,配合tail -F流式解析实现精准实时告警。

用 server_audit 插件捕获 QUERY_DDL 事件(社区版唯一可行路径)
MySQL 社区版不内置 DDL 审计能力,audit_log 插件在标准社区版中根本不存在——执行 INSTALL PLUGIN audit_log SONAME 'audit_log.so' 必然报错 Plugin 'audit_log' is not loaded。真正能稳定、轻量、结构化捕获 CREATE/DROP/ALTER 等语句的,只有 server_audit 插件,且必须显式启用 QUERY_DDL 类型。
- 先确认插件存在:
SHOW VARIABLES LIKE 'plugin_dir',然后检查对应目录下是否有server_audit.so(常见路径:/usr/lib/mysql/plugin/server_audit.so或/usr/lib64/mysql/plugin/server_audit.so) - 安装不需重启:
INSTALL PLUGIN server_audit SONAME 'server_audit.so' - 必须设事件类型:
SET GLOBAL server_audit_events = 'CONNECT,QUERY_DDL'——QUERY_DML无效,ALL会混入海量连接日志,徒增解析负担 - 日志默认写入
/var/log/mysql/audit.log,但务必显式配置:SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log',并确保目录存在、属主为mysql、有写权限
从 audit.log 提取 DDL 并触发实时报警(避免 grep + cron 的低效轮询)
server_audit 日志是结构化文本,每行含 user、host、query、status(0=成功,非0=失败),可直接用流式工具解析,无需先落盘再扫描。
- 推荐用
tail -F /var/log/mysql/audit.log | grep --line-buffered 'QUERY_DDL' | while read line持续监听,配合jq或简单awk提取关键字段 - 示例过滤逻辑(只报失败的 DDL):
tail -F /var/log/mysql/audit.log | awk -F',' '$5 ~ /status: [1-9][0-9]*/ {print $2,$3,$4,$5}' | while read user host query status; do echo "DDL FAILED: $user@$host executed '$query' with status $status" | curl -X POST https://your-alert-webhook/notify; done - 切忌用
grep 'CREATE|ALTER|DROP'原始匹配:大小写、空格、换行都可能漏掉真实语句;而QUERY_DDL是插件打的统一标签,精准可靠 - 若需区分库表粒度,可在
query字段里用正则提取(如awk '{match($4, /CREATE TABLE ([^ ]+)/, arr); print arr[1]}'),但注意字段分隔符是逗号,部分语句含逗号需谨慎处理
为什么不能用 general_log 或 binlog 替代?
general_log 看似能抓到 DDL 文本,但它不是审计日志:
- 不记录执行结果(
ALTER TABLE nonexistent_db.t ADD COLUMN x INT失败了也照记,无status字段) - 所有语句混在一起,
SELECT和CREATE DATABASE同一行,无法自动归类或告警 - 开启后 QPS 下降 15–30%,日志体积小时几百 MB,磁盘 I/O 成瓶颈
binlog 记录的是已生效的变更,但:
- 不含原始 SQL(
ROW格式下完全不可读,STATEMENT格式才可见,但需用mysqlbinlog --base64-output=DECODE-ROWS -v解析) - 不记录失败的 DDL(比如权限不足被拒绝的
CREATE USER) - 时间戳是事务提交时间,不是客户端发起时间,与操作人溯源脱节
报警链路中容易被忽略的三个断点
-
server_audit_logging 默认是 OFF,安装插件和设事件后,必须显式执行 SET GLOBAL server_audit_logging = ON,否则日志文件始终为空
- 日志路径目录若不存在或权限不对(如属主不是
mysql),MySQL 不报错,但 silently 不写入——ls -l /var/log/mysql 和 tail -f 实时验证必不可少
- 如果数据库走中间件(ProxySQL、ShardingSphere),
host 字段记录的是中间件 IP,不是真实终端用户;真要溯源,得在中间件层加日志或传 X-Real-IP 类 header 到注释里(如 /<em> client=10.0.1.23 </em>/ CREATE TABLE ...),再由审计脚本提取
server_audit_logging 默认是 OFF,安装插件和设事件后,必须显式执行 SET GLOBAL server_audit_logging = ON,否则日志文件始终为空 mysql),MySQL 不报错,但 silently 不写入——ls -l /var/log/mysql 和 tail -f 实时验证必不可少 host 字段记录的是中间件 IP,不是真实终端用户;真要溯源,得在中间件层加日志或传 X-Real-IP 类 header 到注释里(如 /<em> client=10.0.1.23 </em>/ CREATE TABLE ...),再由审计脚本提取 实际跑通这条链路,核心就三件事:插件装对、事件开准、日志流接稳。其余都是围绕这三点补漏。


















