mysql 5.7 升级到 8.0 需重点验证 sql_mode、权限表结构、字符集、gtid 和列统计等兼容性断点,并通过 ansible 合理调用 mysqldump 与初始化命令避坑。

MySQL跨版本迁移前必须验证的兼容性断点
5.7 升级到 8.0 不是“导出再导入”就能跑通的,mysql_upgrade 在 8.0.16+ 已被移除,而 mysqld --upgrade=FORCE 又只适用于同大版本内小版本升级。跨大版本迁移本质是数据重建,不是就地升级。
- 重点检查
sql_mode:8.0 默认启用STRICT_TRANS_TABLES和ONLY_FULL_GROUP_BY,旧库 dump 中的 INSERT/SELECT 可能直接报错 - 用户权限表结构已变:
mysql.user表在 8.0 中字段数翻倍,用mysqldump --all-databases导出的权限语句在 8.0 初始化后执行会失败 - 字符集默认值不同:5.7 默认
utf8(实为utf8mb3),8.0 默认utf8mb4;若 dump 文件没显式指定CHARACTER SET utf8mb4,恢复后可能乱码或截断
Ansible 中控制 mysqldump 输出行为的关键参数
Ansible 的 community.mysql.mysql_db 模块不支持跨版本兼容导出,必须用 command 模块调用原生 mysqldump,且参数不能只写 --all-databases。
- 必须加
--set-gtid-purged=OFF:否则 5.7 含 GTID 的实例 dump 出来,在 8.0 上 source 会因 GTID 不连续报错ERROR 1840 (HY000) - 必须加
--column-statistics=0:8.0.22+ 默认收集列统计信息,但 5.7 dump 不含该语法,恢复时报错Unknown system variable 'column_statistics' - 推荐显式指定字符集:
--default-character-set=utf8mb4,避免依赖服务端配置,也防止 Ansible 控制节点 locale 干扰
Ansible 批量部署时 MySQL 8.0 初始化的绕坑写法
直接运行 mysqld --initialize 生成 root 密码不可控,Ansible 无法获取;用 mysql_install_db 又在 5.7.20+ 被废弃,8.0 完全不支持。
- 改用
mysqld --initialize-insecure --datadir=/var/lib/mysql:跳过随机密码生成,后续用mysql命令重置密码 - 初始化后必须立即启动并执行
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'xxx';:8.0 默认用caching_sha2_password插件,老应用连接会报Client does not support authentication protocol - Ansible 中用
wait_for检测/var/run/mysqld/mysqld.sock存在比检测端口更可靠——socket 文件创建早于端口监听完成
跨版本迁移后最常漏掉的校验项
数据行数一致 ≠ 数据正确。8.0 对 JSON、时间类型、隐式转换更严格,很多“看起来没报错”的迁移实际埋了雷。
- 查
information_schema.INNODB_SYS_TABLES中NAME字段是否含非 ASCII 库名/表名:8.0 要求元数据 UTF8MB4,5.7 可能存的是 latin1 编码的字节流,恢复后表名显示为乱码 - 运行
SELECT * FROM mysql.func;:自定义函数在 8.0 中需重新注册,否则应用调用CALL myfunc()报错FUNCTION xxx does not exist - 检查触发器定义:
SHOW CREATE TRIGGER输出里若有DEFINER=`user`@`host`且该用户不存在,触发器不会激活,但无任何提示
跨版本迁移真正的复杂点不在脚本怎么写,而在哪条 SQL 会在哪台业务库上、以什么上下文执行——同一份 Ansible playbook,对报表库和交易库要分路径处理,不能靠一个 when 条件切分就完事。











