MySQL 5.6 升级到 8.0 实战指南:从准备到踩坑全记录
MySQL 5.6 升级到 8.0 实战指南:从准备到踩坑全记录
如果你还在用 MySQL 5.6,应该已经感受到它带来的压力了。官方早在 2021 年就停止了对 5.6 的安全更新,这意味着任何新发现的漏洞都不会被修复。而另一边,MySQL 8.0 带来的性能提升和新特性又让人心动——根据实际测试数据,8.0 在 OLTP 场景下的吞吐量比 5.6 高出 2 到 3 倍,并行查询能力让大表扫描速度提升 5 到 10 倍。
但升级这件事,说起来容易做起来难。尤其是从 5.6 直接跳到 8.0,中间隔着两个大版本,稍有不慎就是数据丢失、业务中断。这篇文章记录了我从 5.6 升级到 8.0 的完整过程,包括踩过的坑和填坑的方法。
一、最重要的前提:不能直接升
先记住一条铁律:MySQL 5.6 不能直接升级到 8.0。
官方文档明确说明,不支持跨大版本升级。必须走 5.6 → 5.7 → 8.0 的两步路径。
为什么?因为 8.0 的数据字典管理方式和 5.6 完全不同。5.6 的数据字典是文件系统 + MyISAM 系统表,8.0 则全部迁移到了 InnoDB 存储的事务性数据字典中。跳过 5.7 直接升 8.0,数据字典初始化会直接失败。
所以,如果你正在规划升级,先把”两步走”刻在脑子里。
二、升级前的准备工作(这部分占 70% 的工作量)
很多人觉得升级就是”备份一下,跑个脚本”,结果一执行就报错。真正稳妥的升级,准备工作占了七成以上的时间。
2.1 环境审计:搞清楚自己有什么
先登录 5.6 实例,把当前环境摸清楚:
-- 查看版本
SELECT @@version, @@version_comment;
-- 查看关键配置
SHOW VARIABLES LIKE '%character%';
SHOW VARIABLES LIKE '%storage_engine%';
SHOW VARIABLES LIKE '%sql_mode%';
SHOW VARIABLES LIKE '%innodb%';重点关注三个地方:character_set_server(5.6 默认 latin1,8.0 默认 utf8mb4)、default_storage_engine(5.6 可能还是 MyISAM,8.0 全是 InnoDB)、sql_mode(8.0 比 5.6 严格得多)。
2.2 找出所有 MyISAM 表并转换
8.0 已经弱化了对 MyISAM 的支持,系统表全部是 InnoDB。如果 5.6 里还有 MyISAM 表,必须提前转成 InnoDB:
-- 找出所有 MyISAM 表
SELECT table_schema, table_name, engine
FROM information_schema.tables
WHERE engine = 'MyISAM'
AND table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys');
-- 逐个转换
ALTER TABLE your_db.your_table ENGINE = InnoDB;特别注意: 如果有 MyISAM 分区表,8.0 直接不支持。必须先转成 InnoDB 引擎,转换操作在 5.6 上可能会锁表,需要评估执行窗口。
2.3 字符集转换:latin1 → utf8mb4
5.6 默认字符集是 latin1,8.0 默认是 utf8mb4。如果不提前转换,升级后中文可能变成乱码,Emoji 更是存不进去。
-- 检查当前字符集
SELECT schema_name, default_character_set_name
FROM information_schema.schemata;
-- 转换数据库
ALTER DATABASE your_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 转换表(逐个执行)
ALTER TABLE your_db.your_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;注意:utf8 在 MySQL 里只支持 3 字节,存不了 Emoji。必须用 utf8mb4(4 字节)。
2.4 检查 sql_mode 中的废弃参数
8.0 移除了多个旧版 sql_mode 值,最典型的是 NO_AUTO_CREATE_USER,5.6 里可能还在用,8.0 里已经彻底没了。如果 5.6 的配置里显式设置了这些值,升级后 MySQL 启动会直接失败。
-- 查看当前 sql_mode
SELECT @@global.sql_mode;推荐的 8.0 安全最小集:
sql_mode = "STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,REAL_AS_FLOAT"注意: ONLY_FULL_GROUP_BY 在 5.6 是可选的,但在 8.0 里默认开启。如果你的业务 SQL 里有 SELECT 了 GROUP BY 未包含的列,在 5.6 只是警告,8.0 直接报错。这个问题在升级前就要排查清楚。
2.5 检查是否存在与 8.0 系统表同名的用户表
8.0 在 information_schema 和 performance_schema 中新增了大量以 INNODB_ 开头的系统视图。如果你的业务库里刚好有同名的表,升级后会导致查询失败或元数据混乱。
-- 找出冲突表
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name REGEXP '^INNODB_'
AND table_schema NOT IN ('information_schema','performance_schema','mysql');找到后必须重命名:
RENAME TABLE mydb.INNODB_LOGS TO mydb.my_innodb_logs;2.6 全量备份 + 搭建测试环境
备份是最后的防线。数据量小可以用 mysqldump:
mysqldump -u root -p --all-databases --routines --events --triggers > all_db_backup.sql数据量大(超过 100GB)建议用物理备份工具如 Percona XtraBackup。
强烈建议: 不要直接在 production 上操作。用备份创建一个新实例,先在测试环境走一遍完整的升级流程,确认业务正常后再动生产库。
三、分步升级实施
第一步:5.6 → 5.7
先把 5.6 升级到最新的 5.7.x(比如 5.7.35 或更高)。这一步相对平稳,因为 5.6 到 5.7 的变化没有 5.7 到 8.0 那么剧烈。
升级完成后,必须执行 mysql_upgrade:
mysql_upgrade -u root -p --force这个命令会检查所有表与当前版本的兼容性,必要时进行修复和升级。确保所有系统表(mysql.plugin、mysql.servers 等)都已转为 InnoDB 引擎。
验证 5.7 运行正常后,再进入第二步。
第二步:5.7 → 8.0
用 MySQL 8.0 的二进制文件替换 5.7。首次启动 8.0 时,加上 --upgrade=FORCE 参数:
mysqld --upgrade=FORCE ...这一步会触发数据字典的迁移转换——从 5.6/5.7 的文件系统 + MyISAM 系统表,迁移到 8.0 的 InnoDB 事务性数据字典。这是整个升级过程中最关键也最容易出问题的一步。
启动成功后,再次执行 mysql_upgrade 完成最后的收尾工作。
四、配置文件(my.cnf)的调整
5.6 的配置文件不能直接拿来给 8.0 用。8.0 启动时如果遇到被移除的参数,会直接报 unknown variable 然后卡死。
必须删除的废弃参数:
query_cache_type和query_cache_size—— 查询缓存已在 8.0 中完全移除- 所有
slave_*开头的参数 —— 8.0 已全部替换为replica_*形式 innodb_use_sys_malloc—— 8.0 强制停用old_passwords—— 已废弃
必须新增的参数:
default_authentication_plugin=mysql_native_password—— 8.0 默认是caching_sha2_password,但很多旧客户端(PHP 7.3、WordPress 5.7 等)连不上,必须改回mysql_native_passwordcharacter-set-server=utf8mb4和collation-server=utf8mb4_unicode_ci—— 明确指定字符集
注意: mysqld --validate-config 只能检查语法和参数是否存在,不能验证实际行为是否符合预期。比如它不会告诉你 explicit_defaults_for_timestamp 设为 OFF 会导致 TIMESTAMP 列默认值逻辑和 5.6 不一致。所以配置改完后,一定要在测试环境里实际跑一遍业务。
五、常见问题与解决方案
问题一:认证插件不兼容
现象: 升级后应用程序连不上数据库,报类似 Authentication plugin 'caching_sha2_password' cannot be loaded 的错误。
原因: 8.0 默认使用 caching_sha2_password 认证插件,但旧版客户端(PHP 7.3 及以下、某些旧版 JDBC 驱动等)不支持。
解决方案: 在 my.cnf 中设置 default_authentication_plugin=mysql_native_password,或者逐个修改用户:
ALTER USER 'yourusername'@'yourhost' IDENTIFIED WITH 'mysql_native_password' BY 'yourpassword';问题二:GROUP BY 查询报错
现象: 以前能跑的 SQL 在 8.0 上报错,类似 Expression #1 of SELECT list is not in GROUP BY clause。
原因: 8.0 默认开启 ONLY_FULL_GROUP_BY,而 5.6 默认没有。
解决方案: 要么修改 SQL 语句,让 SELECT 中的非聚合列都出现在 GROUP BY 中;要么在 sql_mode 中去掉 ONLY_FULL_GROUP_BY(不推荐,这是 8.0 的标准行为)。
问题三:数据字典迁移失败
现象: 从 5.7 升 8.0 时失败,报错涉及 #sql-ib*.ibd 文件缺失。
原因: 数据字典的管理存储方式发生了变化,升级到 8.0 需要做迁移转换。某些临时表或 orphan 文件会导致迁移失败。
解决方案: 在 5.7 阶段确保 mysql_upgrade --force 执行成功,所有系统表都已转为 InnoDB。如果仍然失败,检查 datadir 下是否有孤立的 #sql-*.ibd 文件,备份后可以尝试移除(谨慎操作)。
问题四:外键名或 ENUM/SET 超长
现象: 升级过程中报 ER_TOO_LONG_IDENT 或 ER_TOO_BIG_ENUM 错误。
原因: 8.0 对外键约束名长度上限收紧为 64 字符,同时 ENUM/SET 单个元素的最大长度也有了硬性限制。
解决方案: 提前检查并缩短超长的外键名和 ENUM/SET 元素。
六、写在最后
从 5.6 升级到 8.0,本质上不是一次”升级”,而是一次”迁移”。两个版本之间的差异太大了,字符集、存储引擎、数据字典、SQL 模式、认证插件——几乎每一个层面都在变化。
做好三件事,成功率能提高一大半:
- 充分的准备——把 MyISAM 转 InnoDB、latin1 转 utf8mb4、废弃参数清干净,这些事在 5.6 阶段做完,后面就顺很多
- 分步走——5.6 → 5.7 → 8.0,不要跳,每一步都要验证
- 先在测试环境跑一遍——不要在生产环境上赌运气
升级完成后,8.0 带来的性能提升是实打实的。窗口函数、CTE、原子 DDL 这些新特性也会让开发效率提升不少。如果你的 5.6 还在线上跑着,是时候认真规划一次升级了。
2026年8月