MySQL 8.0 要求显式指定 auth_option 且 CREATE USER 与 GRANT 必须分离,5.7 支持 GRANT 隐式建用户并允许省略认证子句;跨版本应先 CREATE USER IF NOT EXISTS,再执行纯 GRANT。

MySQL 5.7 和 8.0 的 GRANT 语法差异在哪
MySQL 8.0 彻底移除了基于 mysql.user 表直接 INSERT/UPDATE 权限的旧路径,所有权限操作必须走 GRANT / REVOKE,且默认启用 sql_mode=STRICT_TRANS_TABLES。5.7 还允许用 INSERT INTO mysql.user 手动写权限(不推荐),但 8.0 会直接报错 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option 或拒绝写入。
关键区别在于:8.0 要求显式指定 auth_option(如 IDENTIFIED BY 或 IDENTIFIED WITH),而 5.7 允许省略;同时 8.0 的 CREATE USER 和 GRANT 必须分离,不能像 5.7 那样在一条 GRANT 里隐式建用户。
- 5.7 支持:
GRANT SELECT ON db.* TO 'u'@'%' IDENTIFIED BY 'p'; - 8.0 必须拆成两步:
CREATE USER 'u'@'%' IDENTIFIED BY 'p';+GRANT SELECT ON db.* TO 'u'@'%'; - 8.0 若用老写法,会报错:
ERROR 1064 (42000): You have an error in your SQL syntax(卡在IDENTIFIED BY位置)
跨版本部署脚本时如何避免 GRANT 失败
不要在初始化 SQL 脚本里混用版本特有语法。最稳妥的做法是:统一先 CREATE USER,再 GRANT,且所有 GRANT 语句不带认证子句。
真实场景中,CI/CD 流水线常一次部署到多个环境(5.7 测试库 + 8.0 生产库),若脚本含 GRANT ... IDENTIFIED BY,在 8.0 上就挂掉;若完全不用认证子句,在 5.7 上又可能因用户不存在而失败(5.7 的 GRANT 不自动建用户,除非加 IDENTIFIED BY)。
- 兼容写法只有:先
CREATE USER IF NOT EXISTS 'u'@'%' IDENTIFIED BY 'p';(5.7/8.0 都支持) - 再执行纯权限赋值:
GRANT SELECT, INSERT ON db.t TO 'u'@'%'; - 注意 8.0 默认开启
caching_sha2_password插件,如果应用连接器不支持(如老版 PyMySQL CREATE USER 'u'@'%' IDENTIFIED WITH mysql_native_password BY 'p';
SHOW GRANTS 输出格式变化带来的解析风险
8.0 的 SHOW GRANTS FOR 'u'@'%' 返回结果里,USAGE 权限不再显示为 GRANT USAGE ON *.* TO ...,而是省略具体权限项;同时角色(role)相关语句(如 WITH ADMIN OPTION)只在 8.0 出现。如果你的运维工具靠正则解析 SHOW GRANTS 输出来生成回滚语句或做权限比对,大概率会在 8.0 下漏权限或误判。
例如,5.7 中 SHOW GRANTS 可能返回:GRANT SELECT ON `db`.* TO 'u'@'%';而 8.0 同一用户若还被赋予了角色,会多出一行:GRANT `role_r`@`%` TO 'u'@'%' —— 这行在 5.7 根本不存在,老工具会直接报错或跳过。
- 别依赖
SHOW GRANTS文本做自动化权限同步,改用查询information_schema.role_edges(8.0)或mysql.user+mysql.db(5.7)等系统表 - 若必须解析输出,先用
SELECT VERSION()判断版本,再分路径处理 - 8.0 中
SHOW GRANTS不再显示列级权限(如GRANT SELECT(id) ON t),实际权限仍生效,但输出被折叠 —— 别误以为权限丢了
权限迁移后连不上?重点查 default_authentication_plugin 和 plugin 字段
从 5.7 升级到 8.0 后,很多“权限明明给了却连不上”的问题,根源不在 GRANT,而在认证插件不匹配。8.0 默认 default_authentication_plugin=caching_sha2_password,而老客户端(尤其是 Java 的 Connector/J 8.0 以下、PHP mysqli 扩展旧版)不支持该插件,握手直接失败,错误日志里往往只有模糊的 Access denied for user,根本看不出是插件问题。
- 检查用户实际用的插件:
SELECT host,user,plugin FROM mysql.user WHERE user='u'; - 临时降级兼容(仅调试用):
ALTER USER 'u'@'%' IDENTIFIED WITH mysql_native_password BY 'p'; - 生产环境应升级客户端驱动,而非长期切回
mysql_native_password—— 后者不支持密码轮换、无安全通道协商 - 注意:修改 plugin 后必须执行
FLUSH PRIVILEGES;,否则内存缓存未更新,新连接仍按旧插件尝试认证
版本差异藏在细节里,比如 plugin 字段值、GRANT 是否允许隐式建用户、SHOW GRANTS 输出结构——这些地方不动手试一遍,光看文档容易漏掉真正卡住你的那行配置。










