MySQL 5.7已于2023年10月结束生命周期(EOL),这意味着它不再接收安全更新,存在数据泄露和漏洞风险。然而,升级到MySQL 8.0并非一键操作,尤其是sql_mode的变更常常成为迁移过程中的"噩梦"。
本文将全面解析MySQL 8.0与5.7在sql_mode方面的差异,并提供详细的升级避坑指南。
一、为什么sql_mode是升级的核心痛点?
sql_mode是MySQL中控制SQL语法和数据验证行为的系统变量。MySQL 8.0引入了更严格的默认SQL模式,以提升SQL标准合规性和防止常见错误。然而,这种严格性往往导致从5.7升级时应用出现兼容性问题。
典型报错场景
使用MyDumper等工具从5.7迁移到8.0时,最常见的错误是:
Variable 'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER'
这个错误的根本原因在于MySQL 8.0完全移除 了NO_AUTO_CREATE_USER选项。当备份文件中包含此设置时,恢复过程会直接崩溃。
二、MySQL 8.0 vs 5.7:sql_mode核心差异对比
1. 已移除的SQL模式
以下选项在MySQL 8.0中已被移除,如果在配置文件中存在,将导致启动失败:
| 移除的SQL模式 | 说明 | 影响 |
|---|---|---|
NO_AUTO_CREATE_USER |
禁止GRANT语句自动创建用户 | 最常见问题,8.0已完全禁止GRANT隐式创建账号 |
DB2, MAXDB, MSSQL, MYSQL323, MYSQL40, ORACLE, POSTGRESQL |
模拟其他数据库语法 | 组合配置,需检查依赖 |
NO_FIELD_OPTIONS, NO_KEY_OPTIONS, NO_TABLE_OPTIONS |
控制SHOW CREATE TABLE输出 | 影响DDL语句显示 |
2. 默认sql_mode变化
MySQL 5.7默认配置:
STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,
ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
MySQL 8.0默认配置(TRADITIONAL等效):
STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,
ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
关键差异: 8.0默认启用STRICT_ALL_TABLES替代STRICT_TRANS_TABLES,且移除了NO_AUTO_CREATE_USER。
3. 严格模式的强化
MySQL 8.0默认启用更严格的模式:
-
数据类型检查更严格:插入不符合列定义的数据将直接报错而非警告
-
日期验证更严格 :默认禁止
0000-00-00等无效日期 -
GROUP BY验证 :
ONLY_FULL_GROUP_BY默认启用,非聚合列必须出现在GROUP BY子句中
三、升级前检查清单
1. 使用MySQL Shell进行兼容性检查
在升级前,务必运行官方提供的检查工具:
mysqlsh> util.checkForServerUpgrade('user@host:3306', {
"password": "password",
"targetVersion": "8.0.39"
})
检查工具会扫描28项潜在问题,包括:
-
过时的
sql_mode标志(如NO_AUTO_CREATE_USER) -
使用
utf8mb3字符集(建议迁移到utf8mb4) -
超过64字符的外键约束名
-
使用已移除的函数或语法
2. 手动检查sql_mode配置
-- 查看当前全局设置
SELECT @@GLOBAL.sql_mode;
-- 查看当前会话设置
SELECT @@SESSION.sql_mode;
必须清理的配置:
# my.cnf / my.ini 中删除以下内容
[mysqld]
# 删除或修改这一行,移除NO_AUTO_CREATE_USER
# sql_mode='...,NO_AUTO_CREATE_USER,...'
3. 常见组合模式检查
特别注意以下组合模式的展开形式:
| 组合模式 | MySQL 5.7展开 | MySQL 8.0展开 |
|---|---|---|
TRADITIONAL |
包含NO_AUTO_CREATE_USER |
不包含NO_AUTO_CREATE_USER |
ANSI |
宽松模式 | 更严格的标准合规 |
四、升级避坑实战指南
坑点1:迁移工具报错NO_AUTO_CREATE_USER
问题现象:
ERROR: Variable 'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER'
解决方案:
-
修改导出文件(适用于逻辑备份):
删除备份文件中的NO_AUTO_CREATE_USER
sed -i 's/NO_AUTO_CREATE_USER,//g' backup.sql
sed -i 's/,NO_AUTO_CREATE_USER//g' backup.sql -
使用--compact参数(MyDumper):
mydumper --compact --outputdir /backup/dir
-
升级前清理源库配置:
-- 在MySQL 5.7中提前移除
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
坑点2:ONLY_FULL_GROUP_BY导致查询失败
问题现象:
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause
解决方案:
方案A(推荐): 修改查询语句,确保非聚合列功能依赖于GROUP BY列表,或使用ANY_VALUE()函数:
-- 修改前(5.7兼容,8.0报错)
SELECT department, name, MAX(salary) FROM employees GROUP BY department;
-- 修改后(8.0兼容)
SELECT department, ANY_VALUE(name), MAX(salary) FROM employees GROUP BY department;
-- 或
SELECT department, name, MAX(salary) FROM employees GROUP BY department, name;
方案B(临时): 启动时禁用ONLY_FULL_GROUP_BY(不建议长期使用):
[mysqld]
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
坑点3:日期字段0000-00-00报错
问题现象:
ERROR 1292 (22007): Incorrect date value: '0000-00-00' for column
解决方案:
-- 方案1:清理无效日期数据
UPDATE table_name SET date_column = NULL WHERE date_column = '0000-00-00';
-- 方案2:临时允许(仅迁移期间)
SET sql_mode = 'ALLOW_INVALID_DATES';
坑点4:认证插件变更
MySQL 8.0默认使用caching_sha2_password替代mysql_native_password,可能导致旧客户端连接失败。
解决方案:
# 临时回退到旧认证方式(仅迁移过渡期)
[mysqld]
default_authentication_plugin=mysql_native_password
注意: 这应视为临时方案,长期应升级客户端驱动(如JDBC 8.0.9+、PHP 7.4+)。
五、平滑升级策略
推荐升级路径
根据Percona的最佳实践,建议采用滚动升级策略:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MySQL 5.7 │ ──▶ │ MySQL 8.0 │ ──▶ │ MySQL 8.0 │
│ 主库 │ │ 从库(升级) │ │ 主库(切换) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└────────────────────┴────────────────────┘
保留5.7从库作为回滚路径
关键步骤:
-
先升级一个从库,验证应用兼容性
-
在8.0从库下再挂一个5.7从库,作为降级逃生通道
-
业务低峰期切换主库到8.0
-
观察稳定后再升级剩余从库
升级前必做检查
-
备份策略:全量逻辑备份 + 物理备份
-
检查保留关键字:确保没有表/列名使用8.0新增保留字
-
检查存储引擎:移除RocksDB分区表(如使用)
-
检查系统表依赖 :确认应用不依赖
mysql.proc等已移除的系统表
六、总结与建议
| 检查项 | 优先级 | 操作建议 |
|---|---|---|
清理NO_AUTO_CREATE_USER |
🔴 高 | 升级前必须从配置中移除 |
检查ONLY_FULL_GROUP_BY |
🔴 高 | 测试所有GROUP BY查询 |
| 验证日期字段有效性 | 🟡 中 | 清理0000-00-00数据 |
| 更新客户端驱动 | 🟡 中 | 确保支持caching_sha2_password |
| 字符集迁移到utf8mb4 | 🟢 低 | 建议同步进行 |
MySQL 8.0的sql_mode变更是为了提升数据完整性和安全性,虽然短期内可能带来兼容性挑战,但长远来看有利于应用质量提升。
遵循本文的检查和升级策略,可以有效规避"sql_mode"噩梦,顺利完成从5.7到8.0的迁移。