首先确认binlog可用:log_bin必须为ON且binlog_format为ROW,否则转向检查含mysql库的全库备份;再通过mysqlbinlog定位删除前最后一次INSERT/UPDATE快照,拼成INSERT语句并SET sql_log_bin=0执行,最后FLUSH PRIVILEGES。

确认 binlog 是否能用,否则直接放弃
恢复误删用户的第一步不是翻日志,而是看有没有恢复的“原材料”。MySQL 用户信息只存在 mysql.user 表里,没备份、没 binlog,就等于数据物理丢失——不是操作不对,是根本没东西可捞。
必须立刻验证两件事:
-
SHOW VARIABLES LIKE 'log_bin';返回ON才行;返回OFF就不用往下看了 -
SHOW VARIABLES LIKE 'binlog_format';必须是ROW;MIXED或STATEMENT无法还原authentication_string、password_last_changed这类关键字段
如果其中任一条件不满足,跳过所有 binlog 操作,转去检查是否有 mysqldump --all-databases 备份(注意:要包含 mysql 库,很多脚本会加 --ignore-database=mysql 把它漏掉)。
定位并提取被删用户的完整行快照
别用 grep "DELETE FROM mysql.user" 直接搜,mysqlbinlog 输出里它被编码成 ### DELETE FROM `mysql`.`user`,后面跟着多行 ### @1=... 字段快照。真正要找的是删除前最后一次对该用户的写入——通常是 INSERT 或 UPDATE,里面含全部字段值。
推荐实操流程:
- 先缩小时间范围:
mysqlbinlog --base64-output=DECODE-ROWS -vv --start-datetime="2026-08-12 14:00:00" --stop-datetime="2026-08-12 14:30:00" /var/lib/mysql/mysql-bin.000015 | less - 在
less里搜索### INSERT INTO `mysql`.`user`或### UPDATE `mysql`.`user` - 找到目标用户(如
User='appuser' AND Host='%')对应的完整块,复制所有### @N=...行 - 手动拼成
INSERT INTO mysql.user (...) VALUES (...),注意字段顺序必须和DESCRIBE mysql.user一致
特别注意 MySQL 8.0 的 password_last_changed 字段:不能为 NULL,否则 FLUSH PRIVILEGES 后仍无法登录;若日志里是空值,得替换成 NOW()。
执行插入前必须设 sql_log_bin=0
直接执行 INSERT INTO mysql.user 会把操作又记进 binlog,可能触发循环复制或污染日志。更关键的是,如果你正在主从架构上操作,这条语句会同步到从库——但你还没在从库上做同样准备,容易导致权限不一致。
安全做法只有一步:
- 登录后第一件事:
SET sql_log_bin = 0; - 然后执行构造好的
INSERT语句 - 最后必须执行:
FLUSH PRIVILEGES;—— 不是可选项,MySQL 不会自动重载内存中的权限缓存
执行完立刻 SET sql_log_bin = 1; 恢复记录,避免遗漏后续操作。
root 被删时不能靠 CREATE USER 硬建
MySQL 8.0 删除 root 后,CREATE USER 'root'@'localhost' 基本会失败:安全模式下它仍校验认证插件、密码策略等内部约束,且默认插件 caching_sha2_password 和老客户端不兼容。
正确路径是绕过权限系统后手动补全字段:
- 启动时加参数:
sudo mysqld --skip-grant-tables --skip-networking &(Linux/macOS) - 连进去后不 CREATE,而是:
UPDATE mysql.user SET authentication_string='', password_last_changed=NOW(), account_locked='N' WHERE User='root' AND Host='localhost'; - 再批量开权限:
UPDATE mysql.user SET Select_priv='Y', Insert_priv='Y', Grant_priv='Y', Super_priv='Y' WHERE User='root' AND Host='localhost'; - 最后强制指定插件:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_new_pass';
最易忽略的一点:改完必须退出、删掉 skip-grant-tables、重启 mysqld;否则新密码和插件设置不会持久化。


















