应使用 show grants 逐个导出权限,而非直接导出 mysql.user 表;因真实权限分散在多张系统表且结构随版本变化,硬导易丢权限、锁账号或致登录失败;show grants 才能生成人眼可读、机器可执行的完整授权语句。

直接用 SHOW GRANTS 逐个生成,别碰 mysql.user 表
导出权限不是 dump 一张表的事。mysql.user 只存密码哈希和账号状态,真正权限分散在 mysql.db、mysql.tables_priv 等至少 5 张表里,结构随版本变化(比如 5.7 的 password 字段在 8.0 已被 authentication_string 替代),硬导会丢权限、锁账号、甚至导致登录失败。SHOW GRANTS FOR 'u'@'h' 才是唯一能还原“人眼可读、机器可执行”的完整授权逻辑的方式。
批量导出所有非系统用户的 SHOW GRANTS 语句
终端一行命令搞定(Linux/macOS):
mysql -Nse "SELECT CONCAT('SHOW GRANTS FOR ''', user, '''@''', host, ''' ;') FROM mysql.user WHERE user NOT IN ('mysql.infoschema','mysql.session','mysql.sys','root') AND user NOT LIKE 'performance_schema%'" | mysql -N | sed 's/$/;/g'
-
-N关闭列名输出,-s去除多余空格,避免解析失败 - 过滤掉内置用户(
mysql.session等)——否则SHOW GRANTS会报错中断 -
sed 's/$/;/g'给每行末尾补分号,方便后续直接 source 执行 - 输出结果就是一串可直接在目标库运行的
GRANT ... TO 'u'@'h';语句
MySQL 8.0+ 必须额外处理角色和认证插件
如果源库用了角色(CREATE ROLE + GRANT role_name TO 'u'@'h'),SHOW GRANTS 输出里会有 SET DEFAULT ROLE,但不会展开角色本身的权限。漏掉角色定义,导入后权限不生效。
- 先导出角色权限:
SHOW GRANTS FOR ROLE 'role_name'; - 再导出角色绑定关系:
SELECT * FROM mysql.role_edges WHERE TO_HOST = '%';(注意 host 匹配) - 检查用户认证插件:
SELECT user, host, plugin FROM mysql.user;—— 若目标环境客户端老旧(如 PHP 7.2),需在新库执行ALTER USER 'u'@'h' IDENTIFIED WITH mysql_native_password BY 'pwd';降级插件
导入前清空目标库旧权限比盲目追加更安全
直接执行导出的 GRANT 语句大概率报错:ERROR 1396 (HY000): Operation CREATE USER failed(用户已存在),或权限叠加引发越权。
- 推荐做法:先对每个要迁移的账号执行
DROP USER IF EXISTS 'u'@'h';(MySQL 8.0.13+ 支持) - 若版本太低,改用
REVOKE ALL PRIVILEGES ON *.* FROM 'u'@'h'; REVOKE GRANT OPTION ON *.* FROM 'u'@'h'; - 导入后必须执行
FLUSH PRIVILEGES;,内存权限缓存不刷新,新语句等于没执行 - 特别注意:
REVOKE不重置密码、不解除锁定、不修改过期状态——这些字段得单独查mysql.user补全
实际迁移时最容易卡在「以为导出了就完事」——SHOW GRANTS 输出的是授权快照,但用户是否存在、密码是否兼容、角色是否定义、host 白名单是否适配目标网络,这四件事缺一不可。











