直接备份恢复mysql 5.7权限表极大概率失败,因字段语义、默认值、密码哈希逻辑及插件依赖不兼容;正确做法是用show grants逐个生成可执行grant语句。

直接备份并恢复 mysql 库里的权限表(如 user、db、tables_priv)在 MySQL 5.7 上**极大概率失败**,不是因为语法错,而是字段语义、默认值、密码哈希逻辑和插件依赖不兼容。别碰 mysqldump mysql user 这种操作。
为什么不能直接 dump mysql.user 表
MySQL 5.7 的 user 表里有 plugin、authentication_string、password_expired 等字段,它们的值依赖服务端运行时状态;比如 authentication_string 是加密后的密码哈希,但不同版本对空密码、过期标记的处理方式不同;plugin 字段若为 caching_sha2_password(虽 5.7 默认仍是 mysql_native_password,但升级后可能已切换),新实例没加载对应插件就无法解析该用户。
- 导出时用
mysqldump --no-create-info mysql user,导入时会报Field 'password_last_changed' doesn't have a default value或类似缺失字段错误 - 即使绕过字段校验强行插入,用户登录时也可能报
Access denied for user,因为authentication_string值在目标实例上下文里无效 -
mysql.db、mysql.tables_priv等表含 host 匹配逻辑,若新服务器启用了skip_name_resolve=OFF(默认关闭),DNS 解析行为变化会导致权限“存在却不起作用”
正确做法:用 SHOW GRANTS FOR 逐个生成可执行语句
这是唯一能跨 5.7 实例、不依赖表结构、且权限语义完全一致的方式。核心是把权限还原成人类可读、MySQL 可执行的 GRANT 语句,而非二进制数据。
- 先查出所有非系统账号:
SELECT CONCAT('\'', user, '\'@\'', host, '\'') FROM mysql.user WHERE user NOT IN ('mysql.infoschema','mysql.session','mysql.sys','root'); - 对每个账号执行:
SHOW GRANTS FOR 'u1'@'192.168.%';,结果直接就是可复制粘贴执行的 SQL - 注意
WITH GRANT OPTION:如果某条GRANT含这个子句,必须确保该账号在目标库已存在(否则执行会报错),建议先用CREATE USER 'u1'@'192.168.%';创建空账号,再执行GRANT - 若账号有角色(ROLE)或代理用户(PROXY),需额外执行
SHOW GRANTS FOR ROLE 'rolename';和查mysql.role_edges表
如果坚持要 dump mysql 库,必须加过滤和降级参数
仅限你明确知道源/目标都是纯净 5.7 且未启用任何 8.0+ 特性(如角色、caching_sha2_password)时才考虑。即便如此,也必须手动剔除风险字段。
- 导出命令必须带:
mysqldump --skip-triggers --compact --no-create-info --where="user NOT LIKE 'mysql.%'" mysql user db tables_priv columns_priv procs_priv proxies_priv > grants.sql - 导入前,用
sed -i '/authentication_string/d; /password_expired/d; /password_last_changed/d' grants.sql删掉所有含敏感字段的 INSERT 行(这些字段在 5.7 中可能为空但不可省略,删掉后由 MySQL 自动补默认值) - 导入后必须立刻执行:
FLUSH PRIVILEGES;,否则变更不生效 - 导入后检查每个账号能否登录:
mysql -u u1 -h 127.0.0.1 -p,别只看SELECT成功就认为 OK
迁移后最容易被忽略的三件事
权限迁移不是执行完 GRANT 就结束。5.7 下最常漏掉的是:
-
skip_name_resolve配置:新服务器若没显式设skip_name_resolve=ON,MySQL 会对客户端 IP 做反向 DNS 查询,导致'u1'@'192.168.%'匹配失败——哪怕连接 IP 确实是 192.168 段 -
CURRENT_USER()和USER()差异:权限检查用的是CURRENT_USER()(即认证时的 `'u1'@'host'`),但应用代码里如果用USER()做逻辑判断,可能因 DNS 解析结果不同而返回不同 host 名,引发权限误判 - 密码过期策略继承:5.7.4+ 支持
PASSWORD EXPIRE,若源库用户设置了过期,迁移后新库不会自动重置该状态,首次登录会强制改密,影响自动化脚本











