show grants for 'user'@'host' 是最准确的权限导出方式,它自动合并各级权限并处理角色继承;需结合用户列表循环执行,注意definer、密码哈希兼容性及高阶权限版本差异。

直接查 mysql.user 表只能看到账号和密码,看不到权限
很多人导出用户时习惯用 SELECT * FROM mysql.user,但这个表只存认证信息(user、host、authentication_string 等),真正的权限分散在多个系统表里:mysql.db、mysql.tables_priv、mysql.columns_priv、mysql.procs_priv,还有 8.0+ 的 mysql.role_edges 和 mysql.default_roles。想拿到“完整权限清单”,必须拼接这些表,或者换更可靠的方式。
用 SHOW GRANTS FOR 'user'@'host' 是最准的导出方式
MySQL 原生命令 SHOW GRANTS 会按实际生效权限生成可执行的 SQL 语句,自动合并全局、库级、表级等权限,还处理了角色继承(8.0+)。导出所有用户权限,就得对每个用户逐条执行它:
- 先查出所有非系统用户(排除
'mysql.infoschema'、'mysql.session'等内置账号):SELECT DISTINCT CONCAT(''', user, ''@'', host, ''') AS user_host FROM mysql.user WHERE user NOT IN ('mysql.infoschema', 'mysql.session', 'mysql.sys', 'root') AND user NOT LIKE 'mysql\_%'; - 再用脚本循环调用
SHOW GRANTS FOR ...—— 命令行下推荐用mysql -N -s -e避免表头和格式干扰:mysql -u root -p -N -s -e "SELECT DISTINCT CONCAT('SHOW GRANTS FOR ', QUOTE(user), '@', QUOTE(host), ';') FROM mysql.user WHERE user NOT IN ('mysql.infoschema','mysql.session','mysql.sys') AND user != 'root'" | mysql -u root -p > grants.sql - 注意:如果用户有
WITH GRANT OPTION,SHOW GRANTS会明确写出,不能漏;若用户被授予了角色,8.0+ 会额外显示SET DEFAULT ROLE ...行
导出时容易忽略的三个坑
权限导出不是简单 dump 表,几个关键点不处理就会漏权限或还原失败:
-
DEFINER权限不体现在SHOW GRANTS里,但存储过程、视图、事件的DEFINER字段依赖用户存在——导出前得确认目标环境已有对应账号,否则导入后对象可能失效 - 用户密码哈希值(
authentication_string)在 5.7 和 8.0+ 格式不同,用mysqldump --all-databases直接导mysql库会导致密码无法识别,必须用SHOW GRANTS+ 手动CREATE USER ... IDENTIFIED WITH ...分开处理 - 某些权限(如
SYSTEM_VARIABLES_ADMIN、BACKUP_ADMIN)在旧版本 MySQL 不支持,若从 8.0 导出到 5.7,GRANT语句会报错,得提前过滤或降级替换
自动化导出建议用 Python 脚本而非纯 shell
shell 处理空格、特殊字符(比如用户名带连字符、host 是 IP 段)、多行 SHOW GRANTS 输出容易出错。用 Python + mysql-connector-python 更稳:
import mysql.connector
conn = mysql.connector.connect(user='root', password='xxx', host='127.0.0.1')
cursor = conn.cursor()
cursor.execute("SELECT user, host FROM mysql.user WHERE user NOT IN ('mysql.infoschema','mysql.session','mysql.sys')")
for user, host in cursor:
cursor.execute(f"SHOW GRANTS FOR {user!r}@{host!r}")
for (grant,) in cursor:
print(grant + ';')
cursor.close(); conn.close()
重点是用 {user!r} 自动加引号和转义,避免语法错误;输出每行以 ; 结尾,方便后续直接 source。
真正麻烦的从来不是“怎么导”,而是导出来的权限能不能在另一套环境里原样生效——用户是否存在、密码机制是否兼容、角色是否启用、甚至 SQL mode 差异,都会让一条 GRANT 失效。动手前先 SELECT VERSION() 和 SELECT @@default_authentication_plugin 对齐基础环境。











