ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL 5.7到8.0升级避坑指南:评估、备份与回滚全流程

MySQL 5.7到8.0升级避坑指南:评估、备份与回滚全流程 五一假期前的周五我把一台跑了六年的MySQL 5.7实例升级到了8.0。身边很多同事听说后第一反应都是“你胆子真大”因为在他们印象里大版本升级等于熬夜、背锅、删库跑路。但实际做下来我发现MySQL 5.7到8.0的升级并没有传说中那么恐怖——前提是你真的把“升级前”这段准备时间用足了。MySQL 5.7.44是官方最后一个5.7发行版2023年10月之后官方不再维护5.7分支。也就是说继续让5.7裸奔在生产环境本身就是一种风险。但直接换版本又容易踩到默认认证插件、查询缓存、SQL模式这些行为变化的坑。这篇文章我按实际做事顺序把从评估、体检、备份、执行到回滚的完整思路和踩坑记录都写出来给正准备动手里程碑库的朋友一个可复用的参考。1. 升级前先算好三笔账价值、成本和风险很多团队升级数据库理由只有一句“官方不维护了”。这当然成立但不足以支撑一次生产变更。我习惯把升级拆成三笔账价值、成本和风险算清楚再动手。1.1 别被8.0的新功能冲昏头MySQL 8.0的价值不是“版本号更大”而是实实在在解决了几类业务痛点。窗口函数和CTE公共表表达式让复杂报表SQL有了更干净的写法以前用变量拼接排名、用临时表做递归查询的代码可以重构掉。原子DDL意味着DDL要么成功要么完全回滚不再出现“改了一半表结构、metadata损坏”的中间态。降序索引和不可见索引对特定查询的优化效果非常明显尤其是有倒序排序需求和临时禁用索引验证性能的场景。但新功能只是价值的一部分。8.0真正的进步在底层数据字典从MyISAM文件变成了InnoDB表系统表的崩溃恢复能力更强utf8mb4成为默认字符集默认打开的性能监控更完善redo日志管理也做了重构。1.2 5.7生命周期与升级的真实成本5.7的生命周期在2023年10月画上句号。如果你登录MySQL官网还能看到5.7的下载入口但它已经不会再有新的修复版本。社区里现在经常被问的一个问题是“5.7.44之后为什么又出来5.7.43”其实顺序是反的——5.7.43是倒数第二个版本5.7.44才是官方最后的5.7收尾版本。之后进入EOL阶段官方不再提供bug fix和安全补丁。升级的真正成本不是下载安装包那几分钟而是八类行为差异的适配。最典型的有默认认证插件从mysql_native_password换成了caching_sha2_password查询缓存整个被移除SQL模式里NO_AUTO_CREATE_USER消失系统表mysql.user结构大变。这些改变会让老应用连接失败、让老SQL报错、让老运维脚本失效。这才是“升级成本”的大头。1.3 哪些场景建议先别急如果你们团队符合下面任意一条我建议把升级优先级往后放一放先做技术债清理应用依赖的JDBC驱动还是5.x或PHP用的是老版mysql扩展不是mysqli/PDO业务代码里大量依赖查询缓存加速有人会用SQL_CACHE提示生产环境从来没有做过备份恢复演练或者说不出恢复时间点没有专职DBA数据库出了事全靠网上临时搜答案。在这些条件不满足的前提下强行升级等于把兼容性问题和运维问题全挤在同一个变更窗口里。升级本身可能只要30分钟填坑可能要三天。2. 让mysqlsh提前把坑踩给你看兼容性体检与备份设计升级前最值的投入就是花一两个小时做兼容性检查。这里的“检查”不是用眼睛看而是让工具输出一份问题清单再一条条确认。2.1 用MySQL Shell的升级检查器扫一遍MySQL官方从8.0.11开始在MySQL Shell里内置了一个util.checkForServerUpgrade工具专门用来检查旧实例是否适合升级。你在5.7的服务器上装个MySQL Shell然后连接本机执行检查mysqlsh root127.0.0.1:3306 -- util.checkForServerUpgrade(targetVersion8.0.0)输出的结果会按Error、Warning、Note三个级别列出问题。Error级别意味着升级大概率会被中断比如存在无效的视图、触发器对象缺失、分区表使用了不支持的语法等。Warning级别代表即使能升上去业务也可能出现行为变化比如某些SQL的运行计划和5.7不同。Note则是单纯的提醒。我建议把Error级别的每一条都截个图逐项处理。处理完再跑一遍直到输出“No fatal errors found”为止。这一步能省掉升级当天80%的意外。2.2 手工体检清单工具扫不到的几个细节工具能扫对象结构但扫不到环境层面的潜在问题。我每次升级前都手动确认这几件事-- 1. 当前SQL模式确认没有NO_AUTO_CREATE_USER等8.0不支持的模式 SHOW VARIABLES LIKE sql_mode; -- 2. 哪些表不是InnoDB提前决定转不转 SELECT table_schema, table_name, engine FROM information_schema.tables WHERE table_schema NOT IN (mysql, performance_schema, information_schema, sys) AND engine ! InnoDB;重点说下SQL模式。5.7的默认模式包含NO_AUTO_CREATE_USER8.0已经把它移除了。如果你的my.cnf里显式写了这个模式新版本启动会直接报错。另外还需要检查配置文件里有没有query_cache_type、query_cache_size、innodb_file_format、innodb_large_prefix这些8.0已经废弃的参数有的话全部注释掉。还有一个容易被忽略的细节检查DEFINER。如果库里的视图、存储过程、触发器的DEFINER用户是某个已经在5.7里被删掉的账号升级后执行这些对象会报“access denied”。工具能查出一部分但有些“用户存在但密码过期”的情况工具不一定能发现。2.3 备份设计要按“最坏情况”来备份不能只做一份。我强烈建议物理备份和逻辑备份各一份# 物理备份适合快速恢复整库 xtrabackup --backup --target-dir/backup/mysql-5.7-full xtrabackup --prepare --target-dir/backup/mysql-5.7-full # 逻辑备份适合单独导表/导数据做核对 mysqldump --single-transaction --set-gtid-purgedOFF \ --routines --triggers --events \ --databases db1 db2 /backup/logic.sql注意逻辑备份时我用的是--databases db1 db2而不是--all-databases。原因在于逻辑迁移场景下你并不想导出5.7的系统库mysql、performance_schema等再到8.0里导入——系统库结构差异太大交给8.0自己初始化才是正道。备份做完我还会记录一下当时的binlog坐标SHOW MASTER STATUS;。这是回滚和增量同步的重要锚点。最后一个容易忽视的点是磁盘空间。8.0的数据字典从MyISAM文件转成了InnoDB表系统表空间明显变大。我给自己定的安全线是空闲磁盘空间至少等于当前MySQL数据目录的总大小。比如数据目录有20GB升级前空闲至少得有20GB以上否则升级过程中磁盘写满就麻烦了。3. 两条升级路线的真实取舍原地升级与逻辑迁移升级方案通常分两类原地升级in-place和逻辑迁移。两者没有绝对优劣只有适不适合当前场景。3.1 原地升级停机窗口短但不可逆原地升级的思路是停掉5.7把二进制替换成8.0然后让8.0启动时自动升级数据字典和系统表。升级完成后同一份datadir继续使用。这个方案的优点是停机窗口短——从停库到启动完成通常以分钟计数据量大的话重建数据字典的时间会增加但比导出导入快得多。缺点也很明显数据字典一旦被8.0升级旧版5.7的二进制通常无法再打开这份数据目录。也就是说升级这条路走上去就不好原路返回了。所以做原地升级时我坚持一个原则旧版二进制目录不要删。比如/opt/mysql/mysql-5.7.44/ # 旧版本保留不覆盖 /opt/mysql/mysql-8.0.36/ # 新版本放这里用软链切换万一升级过程中数据字典本身没被破坏只是启动卡住还有机会用旧二进制做现场诊断。如果数据字典已经被改写就必须靠备份回滚了。3.2 逻辑迁移旧库原样保留回滚代价低逻辑迁移是另起炉灶装一个全新的8.0实例把5.7里的业务数据导出导入到8.0再重建用户和授权。最大优点是旧库保持原样升级失败不影响线上可以随时切回。同时也适合“想借升级顺便换机器、换操作系统、调整表结构”的场景。具体操作并不复杂新机器上部署8.0初始化实例确认能正常启动在5.7上用mysqldump --single-transaction --set-gtid-purgedOFF --databases db1 db2导出业务库在8.0上执行mysql backup.sql导入手工重建业务账号和授权注意8.0里要先用CREATE USER再GRANT应用切换连接指向新库。逻辑迁移的缺点是耗时取决于数据量。几GB的库还好几百GB甚至TB级的话导出导入可能要跑好几个小时。对于超大库可以考虑用物理层面的Clone插件把InnoDB数据文件复制到新实例再启动自动升级相当于把逻辑迁移的“安全”和原地升级的“速度”结合一下前提是新旧机器网络够好磁盘IO跟得上。3.3 我的选择建议对比维度原地升级逻辑迁移停机窗口短分钟级升级耗时取决于数据量往往以小时计回滚难度难数据字典被改写后需靠备份低旧库原样保留可即时切换环境清淤基本没有沿袭原库可借机换机、重构表结构适合场景核心库、停机窗口紧、已演练充分小库、测试环境、需要在升级期保留旧库如果让我给个通用答案单机小库或者刚准备上云的库直接逻辑迁移干净也省心核心业务库且停机窗口只能批半小时的老老实实做原地升级但必须在测试环境完完整整演练一遍。4. 原地升级执行实录停库、替换、自动升级与常见报错如果你最后选了原地升级下面这份步骤是完全可以照着操作的。4.1 执行前最后几分钟确认状态锁定升级窗口开始后先确认两件事SHOW MASTER STATUS; SET GLOBAL read_only ON;第一件事是记录binlog坐标第二件事是让数据库进入只读防止升级过程中还有新数据写入。理想情况下应用流量已经从网关或中间件层面摘走这里设只读属于双保险。注意升级完成后需要把这改回来。如果你的实例在Windows上操作逻辑类似用net stop mysql停服务替换安装目录后重新注册服务启动时要留意服务指向的是不是新版本的bin路径。4.2 停库、快照与二进制切换停库不要用kill硬杀除非进程已经无响应。优雅关库会让InnoDB完成redo刷盘和binlog轮转升级时少很多不确定性mysqladmin -uroot -p shutdown确认进程退出后对datadir做一次快速快照。我用tar打包是因为简单可靠集群环境也可以走LVM或云盘快照tar czf /backup/datadir-before-upgrade.tar.gz /data/mysql然后解压新版本并切换软链tar xzf mysql-8.0.36-linux-glibc2.12-x86_64.tar.xz -C /opt/mysql/ ln -sfn /opt/mysql/mysql-8.0.36-linux-glibc2.12-x86_64 /usr/local/mysql注意不要在新版本解压完之前把旧目录删掉。新旧目录共存出了问题还有后手。4.3 配置文件的清理动作启动新版本前花五分钟过一遍my.cnf。我的经验是升级失败大半是配置里残留了8.0不认识的参数。重点排查这几类query_cache_type、query_cache_size直接删8.0没有查询缓存sql_mode去掉NO_AUTO_CREATE_USERinnodb_file_format、innodb_large_prefix已废弃删掉skip-grant-tables如果有必须删否则升级逻辑会乱old_passwords删掉。同时可以提前设置default_authentication_pluginmysql_native_password。这算是个人建议如果应用驱动一时半会儿升不动干脆让新实例继续默认使用老认证插件先保证业务不断之后再逐个用户切到caching_sha2_password。4.4 启动与升级日志解读配置清理干净后启动8.0/usr/local/mysql/bin/mysqld_safe --usermysql 这时查看错误日志正常情况下会看到系统表升级的提示。8.0的mysqld在启动时会自动检测版本并升级数据字典不需要你手动跑mysql_upgrade虽然8.0里还留着这个工具但它已经不是必需环节。升级时长和表数量、数据字典大小强相关。如果你的库很小可能几十秒就完成如果实例里几千张表可能要等十几分钟。这个阶段千万不要因为“感觉卡住了”就把进程杀掉多看一眼日志再判断。等日志里出现ready for connections表示升级完成实例可以连了。用SELECT VERSION();确认一下然后用一个只读查询验证基本功能。4.5 常见报错与第一反应我整理了一张升级当天的报错对照表全是实际会遇到的问题报错/现象可能原因处理办法[ERROR] [MY-014060] [Server] invalid mysql server upgradedatadir版本状态异常或升级中途被意外中断先确认datadir能否正常启动旧版本检查前后日志若中断则重新启动新版本必要时用mysql_upgrade修复Unknown variable query_cache_type08.0不识别查询缓存参数删除该参数后重启ERROR 1267 (HY000): Illegal mix of collations5.7旧表与8.0默认排序规则不一致统一数据库/表的collation后再继续客户端报caching_sha2_password cannot be loaded老驱动不支持新认证插件驱动升级或把用户切回mysql_native_password其中[MY-014060]这个错误最吓人我第一次遇到时也是蒙的。后来排查发现多半是datadir里的版本标识和二进制对不上常见于升级过程中人为中断、磁盘问题、或者旧实例暴力杀死导致系统表状态没完成。处理时千万别急着删datadir先做三件事查看完整错误日志前后文、确认文件权限、确认没有其他mysqld进程在占用这个目录。只要datadir本身完整通常重新启动新二进制就能继续。5. 升级后按这个清单核对从认证插件到字符集与慢SQL库起来了不代表升级结束。升级后第一天我从不关心功能是否正常只关心业务表现是否符合预期。这个阶段要按四件事逐项核对。5.1 认证插件兼容是第一个隐藏炸弹8.0里新建的用户默认使用caching_sha2_password插件。老驱动不认识这个插件时连接会直接失败或者报Public Key Retrieval is not allowed类似的错误。全库统一切回老插件的命令很简单ALTER USER appuser% IDENTIFIED WITH mysql_native_password BY password;但我要说明这只是过渡方案。mysql_native_password在后续MySQL版本里已经被官方标记为废弃8.4里默认不启用。真正一劳永逸的做法是升级应用的数据库驱动到8.0系列让应用本身支持新认证插件。否则你只是把兼容性问题推迟到了下一次升级。5.2 字符集与排序规则的连锁反应8.0的新建库默认字符集是utf8mb4默认排序规则是utf8mb4_0900_ai_ci。而5.7时代很多老库用的是utf8mb4_general_ci或latin1。如果两张不同collation的表做JOIN会报Illegal mix of collations。处理方式是在升级前或升级后的低峰期统一排序规则ALTER DATABASE db1 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ALTER TABLE table1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;特别提醒ALTER TABLE ... CONVERT TO CHARACTER SET会重写整张表大表耗时且占IO一定安排到业务低峰执行。5.3 SQL模式与系统表的行为变化升到8.0后有两个行为很多人一时反应不过来。第一GRANT不再隐式创建用户。5.7里你可以直接GRANT ALL ON *.* TO user% IDENTIFIED BY password顺手把用户建了8.0会直接报错。正确做法是先建用户再授权CREATE USER user% IDENTIFIED BY password; GRANT ALL PRIVILEGES ON db1.* TO user%;第二mysql.user表里没有password列了也不要尝试直接改系统表。8.0把用户信息放在InnoDB里操作入口只有CREATE USER、ALTER USER这些官方SQL。那些依赖直接改mysql.user的老运维脚本升级后必须重写。另外如果应用里还在用SQL_CACHE提示8.0直接不支持语句会报错或忽略提示。建议用SHOW FULL PROCESSLIST看一轮正在跑的SQL把这类写法清掉。5.4 升级后顺手做的统计信息刷新8.0的优化器和5.7不完全一样升级后部分SQL的执行计划会变。这不是8.0变差了而是统计信息和优化器逻辑都不同。我通常升级完成后会对业务表重新跑一遍统计信息刷新ANALYZE TABLE db1.table1;表数量多的话可以用information_schema.tables拼一批ANALYZE TABLE语句分批执行。刷新后如果还有明显慢SQL再配合EXPLAIN看执行计划。注意8.0里information_schema的内存占用和5.7有差异查询大库的元数据时别把它当统计表扫很多场景直接看sys库更高效。顺手还要校验几个关键参数innodb_buffer_pool_size是否够用、performance_schema是否开着、innodb_redo_log_capacity用的是默认还是旧的手动配置。8.0.30之后redo日志改成了自动容量管理很多5.7时代的innodb_log_file_size手工配置已经失效了不用再去动它。6. 什么时候该回滚怎么让回滚真的可行回滚这件事理论上越晚决定越被动。我给自己定的原则是升级后一小时内凡是“修复成本小于回滚成本”的问题一律修复凡是“已经开始出现数据不一致”或“数据字典升级失败”的立刻回滚。6.1 区分“可以修复”和“必须回滚”认证插件连不上、字符集排序冲突、慢SQL计划不佳——这些都是能修的问题不值得为此放弃整个升级。但遇到这些情况就必须回滚升级后数据字典升级中途失败而且日志显示系统表状态不完整应用写入出现大面积主键冲突、外键失效或数据不一致升级触发磁盘损坏、InnoDB页损坏修复成本无法估量。第一种情况属于“升级现场已不可靠”第二种属于“业务数据已在错误状态”。这时候继续原地折腾只会把问题放大。6.2 回滚的核心升级前备份的存在意义原地升级的回滚不是“把二进制换回5.7然后启动”那么简单。数据字典一旦被8.0改写旧版mySQL大概率打不开。真正的回滚路径是把备份恢复成一个新的5.7实例。物理备份恢复示例xtrabackup --copy-back --target-dir/backup/mysql-5.7-full chown -R mysql:mysql /data/mysql如果是逻辑备份则用5.7二进制重新初始化实例后导入logic.sql。恢复完检查SHOW MASTER STATUS的坐标和升级前记录是否吻合确认没有丢数据再切流量。这也是我为什么一直强调备份必须提前做恢复演练。没见过恢复流程的备份在凌晨三点等于一张废纸。6.3 我的三个实战经验关于“平滑升级”我最想说的其实是这三条第一凌晨升级前先在测试环境完整演练一遍记录每一步耗时。演练的不仅是命令还有“报错后的处理路径”。真正看到过一遍[MY-014060]、unknown variable这些错误长什么样生产遇到时才不会慌。第二不要把“版本升级”“数据搬迁”“业务重构”三件事叠在一次变更窗口里。单线操作出了问题定位范围很小三线并行出错你连问题在哪一层都说不清。第三平滑升级的含义不是“零风险”而是风险可预期、可控制、可回滚。只要你把备份做扎实、兼容性检查做完整、回滚路径演练过MySQL 5.7到8.0的升级就是一次普通的日常运维而不是一场赌运气的冒险。
返回列表