SHOW GRANTS 是导出用户权限最可靠方式,直接生成可执行授权语句;查 mysql.user 表仅得全局权限,库级、表级等需联合多表,且字段版本不一、含义复杂,易出错。

用 SHOW GRANTS 导出权限,别碰 mysql.user 表
直接 SELECT * FROM mysql.user 只能拿到全局权限字段(比如 Select_priv),但库级、表级、列级、过程权限全都不在里头,拼 SQL 极易漏掉。更麻烦的是,不同版本字段名不一致(Password vs authentication_string)、值含义复杂(如 max_questions 是数字,ssl_type 是字符串),人工处理基本等于埋雷。
真正可靠的方式是让 MySQL 自己生成可执行语句:SHOW GRANTS FOR 'u'@'h' 输出的就是标准、完整、带上下文的授权语句,包括角色、PROXY、列级限制等所有细节。
- 逐个用户导出:运行
mysql -Nse "SELECT CONCAT('\'',user,'\'@\'',host,'\'') FROM mysql.user WHERE user NOT IN ('root','mysql.session','mysql.sys') AND user != ''" | while read u; do echo "SHOW GRANTS FOR $u;"; done | mysql > grants.sql - 注意账号权限:执行命令的连接用户必须对
mysql库有SELECT权限,否则会报Access denied - 过滤无意义语句:
grep -v "USAGE" grants.sql > clean_grants.sql,避免导入一堆空权限
还原前必须先 DROP USER,不能依赖 GRANT 自动建用户
MySQL 8.0+ 默认关闭了 GRANT 隐式创建用户的逻辑,而且新旧版本密码插件可能不兼容(比如源库用 mysql_native_password,目标库默认 caching_sha2_password)。如果跳过清理,直接 source grants.sql,大概率遇到 ERROR 1133 (42000): Can't find any matching row in the user table 或登录失败。
- 先批量生成
DROP USER:用SELECT CONCAT('DROP USER ''',user,'''@''',host,''';') FROM mysql.user WHERE user NOT IN ('root','mysql.session','mysql.sys');,在目标库执行 - 再手动补
CREATE USER:因为SHOW GRANTS不输出密码和认证插件信息,得从源库查mysql.user的authentication_string和plugin字段,补成类似CREATE USER 'u'@'h' IDENTIFIED WITH caching_sha2_password BY '$A$...'; - 执行后务必
FLUSH PRIVILEGES;,否则内存权限不会更新
导入时分两步走:CREATE USER 和 GRANT 分开执行
把所有语句混在一起跑,失败后很难定位是用户没建好,还是某条 GRANT 指向了不存在的库/表/角色。拆开执行能控制节奏、快速验证。
- 先只导入用户定义:
grep "^CREATE USER" clean_grants.sql | mysql -u root -p - 再导入授权语句:
grep "^GRANT " clean_grants.sql | mysql -u root -p - 检查目标环境依赖:如果
grants.sql里有GRANT ... TO ROLE 'r',得确认目标库已存在该角色;如果有GRANT PROXY ON ...,要检查check_proxy_users是否开启 - 跳过失效授权:用
grep -E "GRANT.*ON `[^`]+`" clean_grants.sql | while read g; do db=$(echo "$g" | sed -n 's/.*ON `\([^`]*\)`.*/\1/p'); mysql -Nse "SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='$db'" >/dev/null || echo "# SKIP: $g"; done快速筛掉对不存在库的授权
mysqldump 导权限表不是不行,但必须清洗密码和字段
用 mysqldump -u root -p --no-data --skip-triggers mysql user db tables_priv ... 能导出原始数据,但它输出的是 INSERT 语句,含加密后的 authentication_string。直接导入 8.0+ 实例会失败——缺 plugin、account_locked 等字段,或哈希格式不被识别。
- 若坚持用 dump,必须脚本清洗:提取
user表中user、host、authentication_string、plugin四列,转成带IDENTIFIED WITH的CREATE USER语句 - 禁用
--no-create-info:否则还原时可能因表结构差异报错;但要确保目标库mysql库版本兼容(比如 5.7 dump 不能直接灌进 8.0) - 比
mysqldump更省事的选择是pt-show-grants:它自动适配版本差异,输出即用语句,且能跳过系统保留用户
真实迁移中最容易被忽略的点是:权限语句本身没有语法错误,也不报错,但部分权限在目标环境静默失效——比如针对已删库的授权、未启用的 PROXY 支持、角色未预创建、甚至 sql_mode 不同导致某些函数权限无法解析。验证不能只看是否执行成功,得用 SHOW GRANTS FOR 'u'@'h' 对比源库输出,一条一条核。


















