二进制日志需启用且格式为statement或mixed才能记录grant等权限语句;row格式下仅存行事件,须结合备份与手动还原;恢复前须验证语句并注意host匹配及sql_mode兼容性。

确认二进制日志是否启用且包含权限操作
MySQL 的 GRANT、REVOKE、DROP USER 等权限变更语句只有在二进制日志开启(log_bin = ON)且使用 STATEMENT 或 MIXED 格式时才会被记录。如果启用了 binlog_format = ROW,权限表(如 mysql.user、mysql.db)的变更不会以 SQL 形式写入 binlog,而是以行事件形式存在——这意味着你无法直接提取可读的 GRANT 语句。
- 执行
SHOW VARIABLES LIKE 'log_bin';确认日志已启用 - 检查
SHOW VARIABLES LIKE 'binlog_format';:若为ROW,跳过“提取 SQL”步骤,改用mysqlbinlog --base64-output=DECODE-ROWS -v查看行变更,但需手动还原(难度高) - 确认权限操作发生在最近一次全量备份之后,且对应 binlog 文件未被
PURGE或自动清理
定位误删操作所在的 binlog 文件和位置
误删用户权限通常由 DROP USER、DELETE FROM mysql.user 或批量 REVOKE 引起。你需要先找出该操作发生的时间点或事务位置。
- 用
mysqlbinlog --no-defaults /path/to/mysql-bin.000001 | grep -A 5 -B 5 "DROP USER\|DELETE.*mysql\.user\|REVOKE"快速筛选可疑语句 - 若知道大致时间,用
mysqlbinlog --no-defaults --start-datetime="2024-06-15 14:20:00" --stop-datetime="2024-06-15 14:25:00" /path/to/mysql-bin.000001截取时段 - 注意:
DROP USER 'xxx'@'%'会同时清空mysql.user、mysql.db等多个系统表,binlog 中可能有多条DELETE语句,需全部识别
提取并重放权限恢复语句(非 ROW 格式下)
只要 binlog 是 STATEMENT 或 MIXED,且你找到了误删前的 GRANT 语句(或能推导出原始权限),就可以反向构造恢复逻辑。不要直接重放 DROP USER 之前的任意 GRANT——它可能已被后续修改覆盖。
- 用
mysqlbinlog --no-defaults --base64-output=DECODE-ROWS -v /path/to/mysql-bin.000001 | less滚动查看,找到目标用户的最后一条有效GRANT(注意SET @@session.sql_mode上下文,避免语法报错) - 提取完整语句后,**先在测试库执行验证**:例如
GRANT SELECT,INSERT ON app_db.* TO 'app_user'@'192.168.1.%'; FLUSH PRIVILEGES; - 禁止直接在生产库上用
mysqlbinlog | mysql全量重放——很可能把其他正常变更也重复执行一遍,引发新问题
ROW 格式下只能靠备份+binlog 补丁还原
当 binlog_format = ROW 时,你看到的是 ### UPDATE `mysql`.`user` 这类标记,没有原始 SQL。此时无法“恢复权限”,只能“重建权限”。关键不是从 binlog 解析,而是结合最近一次 mysqldump --all-databases --skip-lock-tables --no-tablespaces 备份中导出的 mysql 库快照,再用 binlog 定位到误删时刻前的最后一次权限变更点,手工合并。
- 从备份中导出原用户定义:
sed -n '/CREATE USER.*app_user/,/;/p' mysql_backup.sql - 用
mysqlbinlog --base64-output=DECODE-ROWS -v找出该用户所有INSERT/UPDATE/DELETE对mysql.user的行事件,按server_id和end_log_pos排序,逆向应用(即撤销 DELETE、回退 UPDATE) - 最终仍需手动执行
CREATE USER+GRANT,并确认host字段(如'%' vs 'localhost')与原配置一致——这点极易被忽略,导致连接失败
实际恢复中最容易卡在 host 匹配和 sql_mode 不兼容上,尤其是旧版本 MySQL 导出的 GRANT 在新版本里可能因 NO_AUTO_CREATE_USER 被拒绝,得临时调整 sql_mode 再执行。











