mysql 5.7升级到8.0后三大兼容性问题:①默认启用only_full_group_by导致group by报错;②datetime不再支持current_timestamp默认值;③query_cache_type等系统变量被移除或重命名。

MySQL 5.7 升级到 8.0 后 GROUP BY 报错:Expression #1 of select list is not in GROUP BY clause
这是升级后最典型的兼容性断裂点。MySQL 8.0 默认启用 sql_mode=ONLY_FULL_GROUP_BY,而 5.7 很多环境是关闭的。一旦查询中 SELECT 的字段没出现在 GROUP BY 子句里,就会直接报错。
- 临时修复:执行
SET session sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));(仅当前会话) - 长期方案:重写 SQL,确保所有非聚合字段都显式出现在
GROUP BY中;或检查应用层 ORM 是否生成了隐式依赖旧模式的语句 - 注意:不要在生产配置文件里粗暴删掉
ONLY_FULL_GROUP_BY—— 它能暴露逻辑歧义,比如SELECT name, MAX(age) FROM user GROUP BY dept_id中name可能对应多个值,结果不可预测
CREATE table 中 DATETIME 默认值为 CURRENT_TIMESTAMP 在 8.0 不再合法
MySQL 5.6/5.7 允许对 DATETIME 字段设置 default CURRENT_TIMESTAMP,但这是非标准行为;8.0 严格区分 TIMESTAMP 和 DATETIME 的默认值能力,仅 TIMESTAMP 支持该语法。
- 错误示例:
CREATE TABLE t (ts DATETIME DEFAULT CURRENT_TIMESTAMP);→ 报错Invalid default value for 'ts' - 兼容写法:改用
TIMESTAMP类型,或显式用触发器/应用层填充时间 - 注意:如果表结构由 ORM 自动生成(如 django、laravel),需确认其 MySQL 8.0 兼容版本,老版本可能仍硬编码该非法语法
系统变量名变更导致 my.cnf 启动失败
MySQL 8.0 废弃并重命名了一批配置项,例如 query_cache_type 已被彻底移除(Query Cache 在 8.0 中删除),explicit_defaults_for_timestamp 默认值变为 ON,且不再接受 OFF 值。
- 常见症状:升级后 mysqld 无法启动,错误日志出现
unknown variable 'query_cache_type=0'或Invalid argument: explicit_defaults_for_timestamp - 操作建议:用
mysqld --verbose --help | grep -A 1 "Default options"查看当前版本实际支持的变量;逐行比对旧my.cnf,注释或删除已废弃项 - 特别注意:某些云数据库(如阿里云 RDS)控制台仍显示旧参数名,但后台已屏蔽,不能盲目照填
json 字段索引语法差异:从 GENERATED column 到 JSON_VALUE
5.7 中常通过虚拟列 + 索引模拟 JSON 字段子键查询,例如:ALTER TABLE t ADD COLUMN j_name VARchar(100) AS (json_unquote(json_extract(data, '$.name'))) STORED, ADD INDEX idx_name(j_name);。8.0 引入 JSON_VALUE() 函数,并支持直接在函数表达式上建索引,但语法更严格。
- 8.0 正确写法:
CREATE INDEX idx_name ON t (JSON_VALUE(data, '$.name' RETURNING CHAR(100))); - 不兼容点:5.7 的
json_extract返回 JSON 类型,而 8.0 要求JSON_VALUE显式声明返回类型,否则建索引失败 - 风险提示:升级后原有虚拟列索引仍可用,但若应用改用新语法,需验证查询是否命中索引(
EXPLAIN看key列)
升级不是“换二进制就行”的事,真正卡住的往往是那些跑了一年没动过、但恰好踩在废弃边界上的 SQL 或配置项。建议先用 mysql_upgrade(8.0.16+ 已弃用,改用 mysqld --upgrade)扫描兼容性问题,再结合慢日志和应用错误监控定向修复。