最可靠的方式是执行SHOW GRANTS FOR 'old_user'@'host'获取可执行GRANT语句,替换用户名和主机后执行;切勿直接INSERT mysql.user表,需先显式CREATE USER再授权,并注意sql_mode、字符集及权限上下文一致性。

直接查老用户的GRANT语句最可靠
MySQL没有内置的“复制权限”命令,SHOW GRANTS FOR 'old_user'@'host' 是唯一能完整还原权限的方式。它输出的是可执行的 GRANT 语句,比手动拼权限更安全——尤其当用户有动态库权限、列级权限或角色时,人工梳理极易遗漏。
实操建议:
- 先确认老用户是否存在且可查:
SELECT User, Host FROM mysql.user WHERE User = 'old_user'; - 执行
SHOW GRANTS FOR 'old_user'@'host';(注意必须带@'host',否则可能匹配到多个账号) - 把输出结果里的
GRANT行复制出来,把'old_user'@'host'替换为'new_user'@'new_host' - 替换后直接执行——别漏掉末尾的
;
新用户不存在时,GRANT会自动创建但有陷阱
GRANT 语句在目标用户不存在时会隐式调用 CREATE USER,但默认创建的是空密码用户(MySQL 8.0+ 默认 require_secure_transport=ON 时可能失败),且 host 部分严格按你写的字符串生成——比如写成 'new_user'@'%' 和 'new_user'@'localhost' 是两个完全不同的账号。
常见错误现象:
- 执行
GRANT ... TO 'new_user'@'%'后,用户从本地连不上:因为localhost走 socket 连接,不匹配% - MySQL 8.0 报错
ERROR 1827 (HY000): Password validation failed:新用户密码太弱,而GRANT不允许指定密码 - 权限生效但无法登录:新用户没设密码,而服务器启用了
require_secure_transport
稳妥做法是先显式建用户:CREATE USER 'new_user'@'%' IDENTIFIED BY 'StrongPass123!';,再执行改写后的 GRANT 语句。
避免用INSERT mysql.user表直接抄权限
有人想省事,直接 INSERT INTO mysql.user SELECT ... FROM mysql.user WHERE User='old_user' ——这极其危险。mysql.user 表结构版本间变动频繁(如 MySQL 5.7 到 8.0 增加了 password_reuse_history 等字段),字段顺序、默认值、加密方式(authentication_string vs password)全不同,硬拷贝大概率导致账号不可用或权限错乱。
更隐蔽的问题:
- 权限字段(如
Select_priv)只是布尔标记,不包含库表粒度信息,实际权限还依赖mysql.db、mysql.tables_priv等表 - 角色(ROLE)权限不会出现在
mysql.user中,只存在mysql.role_edges - 直接写表绕过权限校验,可能触发不一致状态(如
FLUSH PRIVILEGES没及时生效)
批量处理时注意SQL_MODE和字符集
如果要脚本化复制几十个用户的权限,SHOW GRANTS 输出的 SQL 可能含中文注释或特殊字符。若目标库 sql_mode 包含 STRICT_TRANS_TABLES,而旧语句里有已废弃语法(如 USAGE 权限后跟括号),执行会失败。
关键检查点:
- 确认源库和目标库的
sql_mode一致,或临时设为宽松模式:SET sql_mode=''; - 导出时用
--skip-column-names --raw避免制表符干扰:mysql -N -r -e "SHOW GRANTS FOR 'old_user'@'host';" - 若涉及中文库名/表名,确保连接字符集是
utf8mb4,否则GRANT语句里的反引号内容可能乱码
真正麻烦的不是复制动作本身,而是权限背后的上下文:host 匹配规则、密码策略、角色继承链、甚至 SELinux 或防火墙对 socket 文件的限制——这些都不会出现在 SHOW GRANTS 里,得靠人去核对。


















