pt-show-grants 怎么批量导出 MySQL 所有用户权限?参数详解与迁移实操
做过数据库运维的人大概都有这种痛苦:新机房搭好了,要迁移几十个 MySQL 账号和它们的权限,手工对着 mysql.user 表敲 GRANT 语句不仅慢,还极易漏掉 WITH GRANT OPTION 这种细节。Percona Toolkit 里的 pt-show-grants 就是为这件事设计的——它把散落在系统表里的权限,反向生成成一份可执行、可读、可审计的 SQL。

一、为什么不直接查 mysql.user 或 mysqldump
三种常见思路的差别要说清楚:
SELECT * FROM mysql.user:拿到的是哈希后的认证串和字段矩阵,Select_priv='Y'这种表示没法直接在新实例执行,还得人工翻译成 GRANT。mysqldump --no-data --routines mysql:会导出整个权限表的数据行,跨版本恢复时经常因为字段缺失/新增报错(例如 5.7 的password列在 8.0 变成authentication_string),风险高。pt-show-grants:逐用户执行SHOW GRANTS FOR ...,把结果按数据库分组排序后输出标准GRANT ... IDENTIFIED BY PASSWORD '...'语句,可以在目标库原样执行,也天然是审计材料。
工具属于 Percona Toolkit(官方仓库 https://docs.percona.com/percona-toolkit/ ),安装方式:CentOS/RHEL yum install percona-toolkit,Ubuntu/Debian apt install percona-toolkit,也可以直接下 tar 包用 perl 跑(依赖 DBI 与 DBD::mysql)。命令验证:pt-show-grants --version。
二、批量导出所有用户权限
最常用的一条:
pt-show-grants --user=root --ask-pass --flush > grants_$(date +%F).sql
--ask-pass交互式输入密码,避免密码进 shell history;也可--password='xxx'(不安全)或写~/.my.cnf。--flush:在文件末尾追加FLUSH PRIVILEGES;,让恢复脚本执行完立即生效。- 输出会自动跳过
root@localhost一类你不想迁移的账号(可用--ignore-user精确控制)。 - 默认按数据库名字母排序,保证 GRANT 的执行顺序稳定,重复运行产出可比对的相同文本,方便进 Git 做权限基线 diff。
只导出部分用户:--only user1,user2(正则可用 --regex);排除:--ignore-user 'repl|monitor'。
三、输出示例与含义解读
-- Grants for repl@10.0.% GRANT REPLICATION SLAVE ON *.* TO `repl`@`10.0.%` IDENTIFIED BY PASSWORD '*6BBC...'; -- Grants for app@localhost GRANT USAGE ON *.* TO `app`@`localhost`; GRANT SELECT, INSERT, UPDATE, DELETE ON `shopdb`.* TO `app`@`localhost`; GRANT SELECT ON `shopdb`.`orders` TO `app`@`localhost`;
三个细节值得注意:
USAGE ON *.*不是“有权限”,它表示只有登录权限、没有任何库级权限,工具仍会输出,是为了让账号在新实例上先存在。- 库级、表级、列级权限被拆成多条语句,顺序是「全局 → 库 → 表 → 列」,必须整段一起执行才等价。
IDENTIFIED BY PASSWORD '*...'带的是哈希串,导出文件等同于密码哈希,必须按机密文件管理(建议chmod 600)。
四、MySQL 8.0 上的写法变化
8.0 起 GRANT 不再隐式创建用户,且认证插件默认 caching_sha2_password。实践中的处理:
- 导出时加
--compat8(较新版本参数名可能写作--compat80,用pt-show-grants --help | grep -i compat确认),工具会拆成CREATE USER ... ; GRANT ...;的形式。 - 跨版本迁移建议显式指定插件:
ALTER USER 'app'@'%' IDENTIFIED WITH mysql_native_password BY '...';,否则老客户端连不上。想全局默认,改my.cnf的default_authentication_plugin。 - 要顺带迁移角色:
pt-show-grants对ROLE支持有限,角色的GRANT ... TO role建议手工用SELECT * FROM mysql.role_edges复核。
五、权限的批量写入与回收
导出是读的活,写回去更要注意顺序:
- 整库重建:
mysql -u root -p < grants_2024-04-12.sql,执行前先备份现有:pt-show-grants > grants_before.sql。 - 批量回收某类权限:把导出文件做正则处理。例如去掉所有
GRANT OPTION:sed -i 's/ WITH GRANT OPTION//g' grants.sql,再重新导入。这类“导出→改→导入”的循环比一条条REVOKE快得多,但一定要在测试库先跑一遍。 - 生成审计报表:结合
SELECT user,host,DB,table_name,priv_type FROM mysql.tables_priv与columns_priv做交叉核对,避免只见库权限、漏了列权限。
六、常见问题与替代方案
- 报错 “Access denied; you need (at least one of) the ... privilege”:账号需要
SELECT ON mysql.*,MySQL 8.0 还需要SHOW VIEW与RELOAD;或直接用 root。 - “Distributed” 权限缺失 / 集群版:TiDB、MariaDB 集群语法有差异,导出脚本不一定能原样执行,务必逐条 review。
- 不想装 Percona Toolkit:纯 SQL 方案是从
information_schema拼语句,例如
SELECT CONCAT('SHOW GRANTS FOR ''',user,'''@''',host,''';') FROM mysql.user WHERE user<>'',把结果再交给 mysql 客户端执行,效果类似但排序、去重、兼容性都要自己补。 - 与 pt-sync-grants / mysqldump 的取舍:只要权限语句,用 pt-show-grants;要连账号资源限制(
max_user_connections)一起迁,则需额外处理mysql.user里的这些列,工具不会自动带上。
七、常见问题 FAQ
问:导出的文件能不能直接给开发用?
答:不能。里面含密码哈希与完整权限拓扑,属于敏感资产。脱敏办法是 sed -E "s/IDENTIFIED BY PASSWORD '[^']*'/IDENTIFIED BY PASSWORD 'REDACTED'/g" 之后再分享。
问:能不能只在主从之间同步权限?
答:可以,pt-show-grants -h 主库IP | mysql -h 从库IP -u root -p 一条管道完成,但生产建议中间落文件,保留一份可回滚的快照。
问:想定期备份权限怎么做?
答:加 cron:10 2 * * * /usr/bin/pt-show-grants --ask-pass --flush > /backup/mysql_grants/grants_$(date +\%F).sql(cron 里 % 必须转义为 \%,这是最常被忽略的细节),再配 7 天轮转删除。
一句话收尾:pt-show-grants 的价值不只是“少打字”,而是把权限变成可以版本化、可以 diff、可以整体重放的文本资产。养成每周导出一份基线的习惯,出问题时你才有回滚的底气。
生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »
