MySQL 8.0+升级后mysql.user表结构变更导致权限失效,需避免直接还原5.7备份,应通过SHOW GRANTS导出再重放权限语句来安全恢复。

升级后mysql.user表结构不兼容导致权限失效
MySQL 8.0+ 升级后,mysql.user 表底层由 MyISAM 改为 InnoDB,且字段大幅调整(如 password 字段被移除,authentication_string 成为主力;plugin 默认值变更;Grant_priv 等布尔字段语义收紧)。直接还原 5.7 的 mysqldump 备份会因字段缺失或类型冲突导致权限列被静默置空,表现为用户能登录但执行 SHOW DATABASES 或 USE mysql 报错 1142。
- 不要用旧版
mysqldump --all-databases直接恢复整个mysql库 - 检查是否出现
Unknown column 'password' in 'field list'或插入后Select_priv值为NULL - 升级后首次启动时若未运行
mysql_upgrade(MySQL 8.0.16+ 已弃用,改由mysqld --initialize或自动迁移触发),系统表元数据可能未对齐
用SHOW GRANTS导出再重放是最安全的恢复方式
绕过表结构差异,只还原逻辑权限。前提是升级前有任意一个高权限用户仍可登录(哪怕只是只读),或你保留了升级前的 user_grants.sql 文件。
- 在旧版本或备份实例中执行:
mysql -u root -p -e "SELECT CONCAT('SHOW GRANTS FOR ''',User,'''@''',Host,''';') FROM mysql.user WHERE User != 'mysql.infoschema' AND User != 'mysql.session' AND User != 'mysql.sys'" | mysql -u root -p --skip-column-names | sed 's/$/;/g' > user_grants.sql - 在新实例中执行:
mysql -u root -p - 必须紧跟执行:
FLUSH PRIVILEGES;—— 否则权限不会加载到内存 - 注意:MySQL 8.0+ 中部分旧权限(如
FILE、SHUTDOWN)需显式授予,且root用户默认不再拥有FILE权限,除非手动加
UPDATE mysql.user 手动补权限字段的风险点
当无法获取旧 SHOW GRANTS 输出时,只能直连 mysql 库更新。但字段名、默认值、校验逻辑已变,硬写一堆 Y 很容易翻车。
- MySQL 8.0+ 不再认
Select_priv='Y'这种字符串赋值,而要求是TINYINT(1)类型,应写Select_priv=1 -
plugin字段必须匹配认证方式:若用 caching_sha2_password(8.0 默认),不能设为mysql_native_password,否则登录报错 1251 -
authentication_string不能为空,即使用户用 socket 认证也要保持原值,清空会导致Access denied - 更新后务必验证:
SELECT User, Host, Select_priv, authentication_string, plugin FROM mysql.user WHERE User = 'your_user';
跳过权限验证模式下修复失败的典型表现
以 --skip-grant-tables 启动后仍无法恢复,往往不是权限没写对,而是 MySQL 拒绝在该模式下执行某些操作。
- 执行
ALTER USER 'root'@'localhost' IDENTIFIED BY 'xxx';会报错ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement - 此时只能用
UPDATE mysql.user SET authentication_string = PASSWORD('xxx') ...(5.7)或UPDATE mysql.user SET authentication_string = '$A$...';(8.0,需先用另一个实例生成哈希) -
FLUSH PRIVILEGES;在 skip 模式下有效,但重启后若mysqld启动失败,大概率是user表某字段类型不合法(如account_locked被设为'Y'而非0或1)
SHOW GRANTS 导出路径,其次才是修表;任何手动 UPDATE 都必须对照当前版本的 DESCRIBE mysql.user; 结果来写。


















