升级mysql需先用mysqldump导出旧版权限语句,禁用建表与扩展插入;升级后须用show grants重建权限并适配认证插件;8.0启动后需重置root密码并确认plugin为mysql_native_password。

升级前必须导出旧版本的权限数据
MySQL 5.7 及更早版本的权限存储在 mysql 库的 user、db、tables_priv 等表中,而 8.0+ 改用角色模型和更严格的账户结构(如 authentication_string 字段替代 password)。直接复制 mysql 库会导致新版本启动失败或权限失效。
正确做法是用 mysqldump 导出逻辑权限语句:
mysqldump --no-create-info --skip-extended-insert -u root -p mysql user db tables_priv columns_priv procs_priv proxies_priv > mysql_grants.sql
-
--no-create-info:避免导出建表语句,只取数据 -
--skip-extended-insert:每行一个INSERT,便于后续 grep 或 sed 过滤 - 务必排除
columns_priv和procs_priv中含特殊字符(如换行、引号)的记录,否则导入会报错ERROR 1064
升级后不能直接导入 dump 文件
MySQL 8.0 的 mysql.user 表字段大幅变更(例如移除了 Password 列,新增 account_locked、password_reuse_history),且默认认证插件从 mysql_native_password 变为 caching_sha2_password。直接执行旧 dump 里的 INSERT INTO mysql.user ... 会触发字段不匹配或认证插件不兼容错误。
应改用 SHOW GRANTS 方式重建权限:
- 对每个用户执行:
SHOW GRANTS FOR 'username'@'host'; - 将输出保存为
grants_user.sql,手动替换IDENTIFIED BY为IDENTIFIED WITH mysql_native_password BY(如需兼容老客户端) - 导入时先创建用户:
CREATE USER 'username'@'host' IDENTIFIED WITH mysql_native_password BY 'pwd';,再执行GRANT ...
跨大版本迁移必须重置 root 密码并校验 plugin
MySQL 8.0 启动后,root@localhost 默认使用 caching_sha2_password,但旧备份中的密码哈希值无法被新插件识别,导致连不上库。这不是权限丢失,而是认证失败。
- 启动时加
--skip-grant-tables,然后执行:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'newpass'; - 检查是否生效:
SELECT host,user,plugin FROM mysql.user WHERE user='root';,确保plugin是mysql_native_password或目标插件 - 若应用连接报
Client does not support authentication protocol,说明驱动太老(如 MySQL Connector/J
自动化迁移脚本要避开 system user 和 expired 账户
MySQL 8.0 内置了 mysql.infoschema、mysql.sys 等系统账户,其 plugin 为 auth_socket 或 sha256_password,且密码为空或不可逆。批量迁移时若未过滤,脚本会尝试重置这些账户,引发服务异常。
安全过滤条件示例(在生成 SHOW GRANTS 命令前):
SELECT CONCAT('SHOW GRANTS FOR ''',user,'''@''',host,''';')
FROM mysql.user
WHERE account_locked='N'
AND password_expired='N'
AND user NOT IN ('mysql.infoschema','mysql.session','mysql.sys');另外,GRANT OPTION 权限在 8.0 中受 role_admin 权限约束,单纯导入 GRANT ... WITH GRANT OPTION 不足以让普通用户授予他人权限,必须额外赋权:GRANT ROLE_ADMIN ON *.* TO 'admin'@'%';
真正麻烦的是存储过程和函数里的 DEFINER,升级后若原定义者不存在,调用会报 ERROR 1449 —— 这类对象得逐个用 ALTER DEFINER 修正,没法靠脚本全自动兜底。










