携程mysql兼容需同一sql在5.7和8.0均正确执行:group by用any_value()或聚合函数;json查询统一用json_extract+字符串比较;驱动分环境打包并指定认证插件;mysqldump加--compatible=mysql56降级语法。

携程这类高并发、多环境、长生命周期的业务系统,MySQL客户端代码必须能同时跑在 5.7 和 8.0+ 环境上——不是“选一个版本适配”,而是同一套 SQL 在两个版本都得正确执行。硬切驱动或强推升级不现实,关键在 SQL 层做兼容性收敛。
GROUP BY 语句在 5.7 和 8.0 上行为不一致怎么办
8.0 默认开启 ONLY_FULL_GROUP_BY,而 5.7 默认关闭;直接写 SELECT user_id, name FROM orders GROUP BY user_id 在 8.0 报错 Expression #2 of SELECT list is not in GROUP BY clause,但在 5.7 悄悄返回任意一条 name。
- 别用
SET SESSION sql_mode = ...动态关模式——携程线上不允许会话级 SQL 模式变更,且掩盖语义歧义 - 统一改用
ANY_VALUE(name):它在 5.7.5+ 和 8.0 都可用,语义明确(“我接受非确定性取值”),且不触发模式校验 - 如果字段是时间类(如
created_at),优先用聚合函数:MAX(created_at)比ANY_VALUE(created_at)更符合业务意图 - ORM 层(如 MyBatis)若生成裸字段 GROUP BY,需在 mapper 中显式包裹
ANY_VALUE(),不能依赖方言开关
JSON 函数跨版本调用失败(JSON_CONTAINS / -> 在 5.6/5.7 不支持)
携程订单、用户画像等模块大量用 JSON 字段存扩展属性,但 JSON_CONTAINS(json_col, '"paid"') 在 5.6 直接报错 FUNCTION JSON_CONTAINS does not exist;data->'$.status' 同样不可用。
- 5.6 环境必须降级:用
JSON_EXTRACT(data, '$.status') = '"paid"'(注意引号)替代JSON_CONTAINS,前提是字段类型为TEXT或VARCHAR(5.6 不支持JSON类型) - 5.7+ 可用
data->>'$.status'(去引号版),但携程内部约定:所有 JSON 路径查询统一走JSON_EXTRACT(col, path)+ 字符串比较,避免->操作符在低版本解析失败 - 应用层封装工具类(如
JsonSqlUtils.contains()),根据运行时SELECT VERSION()返回值自动切换 SQL 片段,比预编译条件更可靠
JDBC 连接串和驱动版本混用导致认证失败
携程后端服务常共用一套连接池配置,但测试环境用 MySQL 5.7,生产切到 8.0,老驱动连不上新库是高频故障点,典型错误:Client does not support authentication protocol requested by server 或 Authentication plugin 'caching_sha2_password' cannot be loaded。
- 驱动必须分环境打包:5.7 环境用
mysql-connector-java:5.1.49,8.0+ 环境用mysql-connector-java:8.0.33;禁止“一个 JAR 跑所有环境” - 连接串强制指定认证插件:
?serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false是危险写法,应改为&defaultAuthenticationPlugin=mysql_native_password(仅当驱动不支持caching_sha2_password时启用) - 数据库侧不改全局
default_authentication_plugin,而是按用户粒度调整:ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'pwd';,避免影响 DBA 工具链
mysqldump 导出 SQL 在不同版本回放失败
携程 CI/CD 流水线中,从 8.0 导出的 SQL 在 5.7 环境初始化失败很常见,比如报错 Unknown collation: utf8mb4_0900_as_cs 或 FUNCTION JSON_ARRAY_APPEND does not exist。
- 导出命令必须加
--compatible=mysql56(不是mysql40),它会禁用 8.0 特有语法(如窗口函数注释、WITH ROLLUP扩展),并把排序规则降级为utf8mb4_unicode_ci - 显式排除 JSON 函数:
--skip-triggers --no-create-info --skip-extended-insert配合应用层建表,避免 dump 包含JSON_CONTAINS等 5.7 不支持的函数 - 字符集声明必须硬编码:
--default-character-set=utf8mb4,否则 8.0 dump 默认用utf8mb4_0900_as_cs,5.7 无法识别
真正难的不是单点语法替换,而是让同一份 SQL 在两个版本下产生**语义一致的结果**——比如 ORDER BY 在不同排序规则下顺序可能翻转,TIMESTAMP 字段在未设 explicit_defaults_for_timestamp 时行为也不一样。这些细节不写进 SQL,只靠驱动或配置很难兜底。











