pt-show-grants是percona toolkit中导出mysql用户grant语句的工具,可直接使用但非一键迁移,需人工处理版本差异、用户存在性、identified with语法等兼容性问题。

pt-show-grants 是什么,能不能直接用在新服务器上
pt-show-grants 是 Percona Toolkit 里的一个工具,作用是把 MySQL 用户的 GRANT 语句原样导出来。它本身不校验语法兼容性、不处理权限上下文,只是“照抄”当前实例里的授权记录。
所以它能用,但不是“一键迁移”——导出的 SQL 很可能在新服务器上执行失败,尤其当目标 MySQL 版本不同、用户已存在、或有 IDENTIFIED WITH 插件差异时。
常见错误现象:
-
ERROR 1396 (HY000): Operation CREATE USER failed for 'u'@'%'(用户已存在) -
ERROR 1064 (42000): You have an error in your SQL syntax(新版 MySQL 不支持旧式密码哈希或插件写法) - 导出结果里混着
SET PASSWORD或ALTER USER ... IDENTIFIED BY,和GRANT顺序错乱,导致权限未生效
实操建议:
- 先确认源/目标 MySQL 版本:SELECT VERSION();
- 若目标为 8.0+,注意 pt-show-grants 默认不处理角色(ROLE)、不识别 mysql.role_edges 表,需额外处理
- 导出前加 --skip-table-privileges 可避免导出数据库级权限(比如对 mysql 系统库的误操作)
怎么安全导出并清理掉“不能直接跑”的语句
pt-show-grants 默认输出含 CREATE USER 和 GRANT,但新服务器上通常只需 GRANT,且得先确保用户存在(或改用 CREATE USER IF NOT EXISTS)。
使用场景:你只想迁移业务用户(如 'app_rw'@'10.20.%'),不碰 root、mysql.sys 这类系统账户。
实操建议:
- 加 --user 参数限定只导指定用户:pt-show-grants --user=app_rw
- 加 --no-create-user 跳过 CREATE USER 行,自己补更可控的创建逻辑
- 用 sed 或 awk 过滤掉带 IDENTIFIED BY PASSWORD 的行(MySQL 5.7+ 已弃用,8.0+ 直接报错)
- 若目标库已存在同名用户,导出后手动把 CREATE USER 改成 ALTER USER,并确保 GRANT 后跟 FLUSH PRIVILEGES;
MySQL 8.0+ 下 pt-show-grants 的兼容性坑
pt-show-grants 最新版(3.5+)支持 MySQL 8.0,但默认行为仍按 5.7 习惯走:比如把 caching_sha2_password 用户导成 IDENTIFIED WITH caching_sha2_password AS '$A$...',而实际建用户时若没显式指定 AS 哈希值,MySQL 会重新算一次,导致密码不一致。
性能 / 兼容性影响:导出过程本身很快,但生成的 SQL 在 8.0 上执行前必须人工检查三处:
-
IDENTIFIED WITH后是否带AS子句(不带就执行失败) - 是否含
USAGE权限以外的全局权限(如SUPER),8.0+ 已拆分为细粒度系统权限,GRANT SUPER会报错 - 是否引用了不存在的数据库或表(比如源库有
db_old,新库已重命名)
实操建议:
- 导出后 grep 检查:grep "IDENTIFIED WITH" grants.sql,补全 AS 值或改用明文密码(仅测试环境)
- 用 mysql --version 和 SELECT * FROM mysql.global_grants;(8.0.16+)交叉验证权限模型
- 对含 ON *.* 的语句,逐条确认目标库是否存在对应对象,否则 GRANT 报错且中断后续执行
替代方案:不用 pt-show-grants,手写 SQL 更稳吗
真要迁移干净,有时手写比依赖工具更可靠——尤其当用户数少(
关键不是“手写多快”,而是避开工具隐含假设:比如 pt-show-grants 认为所有用户都该被 CREATE USER,但它不判断用户是否来自外部认证(LDAP/PAM),也不管 max_connections 等资源限制是否需要同步。
实操建议:
- 查用户定义:SELECT User,Host,plugin,authentication_string FROM mysql.user WHERE User NOT IN ('mysql.infoschema','mysql.session','mysql.sys');
- 查权限:SHOW GRANTS FOR 'u'@'h';(逐个执行,避免漏掉)
- 用 mysqldump --no-data --databases mysql 导出系统库结构,再人工提取 user 和 db 表数据(仅限同版本迁移)
- 所有 CREATE USER 语句末尾加 ACCOUNT LOCK,导入后再 ALTER USER ... ACCOUNT UNLOCK,防误触发
权限迁移最麻烦的从来不是导出,而是“哪些不该导”——比如监控账号的 PROCESS 权限在新集群可能由 Agent 统一管理,硬塞进去反而冲突。










