ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MySQL 5.7升级8.0实战:完整路径、踩坑记录与备份方案

MySQL 5.7升级8.0实战:完整路径、踩坑记录与备份方案 我上周刚帮朋友把一套跑了三年的MySQL 5.7实例升到了8.0整个过程比预想的要顺但中间也踩了软件源、认证插件、sql_mode几个坑。MySQL 8.0发布好几年社区版和企业版都已经非常稳定5.7官方维护也进入了末期从安全补丁和长期维护角度看升级这件事宜早不宜迟。这篇我把自己完整跑过的升级路径、检查脚本、备份方案、踩坑记录都整理出来覆盖Linux和Windows两种常见环境下的操作差异适合手里管着5.7实例、准备往8.0迁的DBA、后端开发和运维同学参考。1. 升级前必做版本差距与风险评估1.1 为什么5.7升8.0不是小事很多同学觉得MySQL升级不就是把安装包换一下、把数据目录指向新版本吗在5.6升5.7的年代这种想法还算可行但从5.7升8.0完全不是同一个概念。8.0是一次大规模重构的版本底层的数据字典从MyISAM表换成了InnoDB原来的frm文件机制彻底移除权限表结构也改了。这意味着MySQL 8.0首次启动时会自动对数据字典做一次重建和升级这个过程不可逆而且一旦开始就不能用旧版binary再去启动旧数据目录。另一个容易忽略的问题是行为变更。8.0默认字符集是utf8mb45.7默认还是utf8mb3默认认证插件换成了caching_sha2_password老客户端驱动可能连不上sql_mode默认值多了STRICT_TRANS_TABLES、NO_ZERO_IN_DATE、NO_ZERO_DATE、ONLY_FULL_GROUP_BY等一堆严格模式。这些变化不会在升级时报错但会在升级后某个不常见的SQL上突然炸出来。注意8.0移除了查询缓存Query Cache相关参数。如果配置文件里还有query_cache_type或query_cache_sizeMySQL 8.0会直接启动失败。所以在动手之前先要搞清楚一件事你的业务SQL和连接驱动是否能接受这些行为差异。数据库升级的表面工作是换版本真正的难点是在升级后让应用无感知。1.2 用官方检查脚本提前扫雷在正式升级之前先用MySQL官方提供的升级检查工具把兼容性问题扫一遍能省掉后面80%的麻烦。MySQL 8.0安装包里自带一个mysqlcheck命令大众用法是检查表但它还有一个隐藏能力——升级预检查。MySQL官方推荐的标准做法如下# 先连接到旧版本5.7实例 mysql -uroot -p # 在MySQL 5.7里执行升级检查SQL mysql USE mysql; mysql CHECK TABLE user, db, tables_priv, columns_priv, procs_priv, proxies_priv, roles_mapping FOR UPGRADE;对5.7版本更完整的做法是用mysql_upgrade --check命令它只检查不实际执行升级。但要注意mysql_upgrade这个工具在8.0里已经被废弃了8.0的自动升级机制取代了它。如果你已经下载了MySQL Shell8.0官方推荐工具那直接用它的util.checkForServerUpgrade()方法最省心-- 在MySQL Shell里执行 mysql-js util.checkForServerUpgrade(rootlocalhost:3306, {password: your_password})这个命令会逐项检查几十个兼容性问题输出结果类似这样Checking for version upgrade ... 3) Incompatible remote tables found No issues found. ... 7) Columns with implicit defaults No issues found. ... Errors: 0 Warnings: 12 Notices: 0输出里的Warnings不一定都是致命的但每一条都值得看一遍。比如它会警告某些表使用了utf8mb3字符集、某些字段用了废弃的zerofill属性、某个存储过程带有DEFINER用户等。这些警告就是升级后可能出现问题的清单提前截图保存。我自己实测的经验是在数据量不大几十GB以内的情况下这个检查只要几分钟。如果实例有几百GB甚至上TB的数据建议在业务低峰期做因为检查会扫描所有表的元数据对IO有一定压力。2. 备份与升级方案选型2.1 无论走哪条路先做一份可靠备份版本升级最忌讳的事情就是不备份直接上。哪怕官方文档说了5.7到8.0的升级路径是支持的你也必须假设升级过程中可能出现意外——比如磁盘满了、断电、InnoDB数据字典重建失败等。备份方案有两种可以都做第一种是用mysqldump做逻辑备份。优点是简单、通用、可靠缺点是慢大库恢复时间长。命令参考如下mysqldump -uroot -p --single-transaction --routines --triggers --events --set-gtid-purgedOFF --all-databases full_backup_2025.sql参数解释--single-transactionInnoDB表用一致性快照不锁表对线上业务影响最小。--routines、--triggers、--events把存储过程、触发器、定时事件都导出来漏一个后面都麻烦。--set-gtid-purgedOFF避免导出文件带上GTID信息方便后续导入到新实例。第二种是用xtrabackup做物理备份。优点是速度快支持增量适合大库场景。恢复方式是把备份文件解压到新数据目录然后启动MySQL自动恢复。提示5.7和8.0的xtrabackup版本不通用。备份5.7必须用Percona XtraBackup 2.4备份8.0要用8.0.x版本千万别混用。我在实际升级前会把这两种备份都做一遍。物理备份放本机逻辑备份再额外拷贝一份到其他机器。万一升级真的搞砸了还能迅速回滚到旧版本。2.2 两条主路径原地升级与逻辑迁移怎么选5.7升8.0有两条主流路径各有优缺点需要根据你的停机窗口和实例规模来选。路径一原地升级In-Place Upgrade操作逻辑很简单停掉5.7把8.0的二进制替换进去启动8.0让它自动升级数据字典。这个方式的优点是快不需要导数据几十GB的库可能只用停机几分钟剩余时间都在做内部元数据升级。缺点是在升级过程中如果遇到问题回滚很麻烦因为数据字典已经变了旧版本无法识别。适用场景停机窗口短、数据量大、你有充分的备份和回滚预案。路径二逻辑迁移Logical Migration先用mysqldump或者MySQL Shell的util.dumpInstance把数据逻辑导出然后安装全新的8.0实例再把数据导入。这种方式最稳等于重新搭一套环境导入前可以顺手清理一下冗余数据、调整表结构还能顺便测一遍新环境。缺点是慢几百GB的库导出导入可能要跑几小时甚至一整天。适用场景数据量不大、停机时间充足、或者你想借升级的机会整理一遍库表结构。在我这次的实际操作中线上那套库大概200GB我衡量了一下选择了原地升级。下面两个章节会把两条路径分别讲清楚你可以根据自己的情况选一条。3. 实战一最省事的原地升级In-Place3.1 环境信息与准备动作先列一下我这次操作的环境操作系统CentOS 7.9Linux环境和一台Windows Server 2012 R2做对照测试旧版本MySQL 5.7.44数据目录在/var/lib/mysql新版本MySQL 8.0.40LTS版本官方长期支持数据量约200GB主要业务表都是InnoDB停机前的准备动作如下第一步把配置文件里的废弃参数清掉。5.7配置里常见的这几个参数在8.0里已经不存在或者默认值变了必须先处理# 需要删除或注释掉的8.0不再支持的参数 # query_cache_type 1 # query_cache_size 67108864 # innodb_large_prefix ON # innodb_file_format Barracuda # innodb_file_format_check ON # innodb_file_format_max Barracuda # innodb_purge_batch_size # max_worker_threads 调整为系统自动第二步检查sql_mode。5.7时代很多开发习惯设置一个宽松的sql_mode比如NO_ENGINE_SUBSTITUTION。8.0默认模式很严格建议升级前先在旧实例上把sql_mode调成8.0默认值跑几天观察业务有没有异常。8.0默认的sql_mode是sql_mode ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION升级前把5.7实例的sql_mode临时改成这个值观察一到两天。如果应用出现报错至少能在升级前发现并修掉而不是在升级后发现。第三步确认磁盘空间。MySQL 8.0升级过程中需要重建数据字典临时文件和undo日志会膨胀建议预留至少数据目录当前大小的1.5倍可用空间。我那次200GB的数据磁盘剩余空间有300GB多跑起来才不慌。3.2 替换二进制并启动升级环境准备好的前提下原地升级的操作用时大概只有二十分钟。首先是干净地停掉旧实例。注意不要直接kill -9MySQL 8.0的升级对数据文件的一致性要求很高systemctl stop mysqld # 或者 service mysql stop停掉之后确认进程已经退出再用ps或者ss确认端口3306已经释放。然后替换二进制文件。如果你是yum或apt安装的直接用官方仓库切换版本# 先备份旧配置 cp /etc/my.cnf /etc/my.cnf.bak_57 # 卸载5.7注意不要删掉/var/lib/mysql yum remove mysql-community-server # 安装8.0 yum install mysql-community-server如果用tar包方式安装先把旧的/usr/local/mysql重命名备份再解压8.0的tar包到原路径确保my.cnf里basedir和datadir路径不变。mv /usr/local/mysql /usr/local/mysql_57_bak tar -xzf mysql-8.0.40-el7-x86_64.tar.gz -C /usr/local/ mv /usr/local/mysql-8.0.40-el7-x86_64 /usr/local/mysql chown -R mysql:mysql /usr/local/mysql接下来就是关键一步启动8.0实例让它自动执行升级。systemctl start mysqld首次启动时MySQL会自动检测到旧版本数据目录然后开始数据字典升级。日志位置在/var/log/mysqld.log这个过程类似[System] [MY-013415] Server socket created on IP: 0.0.0.0:3306 [System] [MY-010116] Server started in daemon mode [System] [MY-013380] Upgrading the data directory. [System] [MY-013381] Data directory upgraded successfully.看到这几行基本就成功了。8.0的升级日志比5.6/5.7时代的mysql_upgrade输出简洁很多没有逐表刷新的过程因为8.0把元数据升级做得更快了。注意8.0首次启动后不要再把数据目录指回5.7的二进制启动否则可能损坏数据字典。升级成功后旧的mysql_57_bak目录可以暂时保留在磁盘上确认稳定运行一周后再删。Windows环境的操作逻辑完全一致只是安装方式不同。下载8.0的zip压缩版解压后把bin目录加入PATH然后用管理员权限打开CMD用mysqld --initialize-insecure初始化新实例如果新数据目录再net start mysql启动。如果升级已有数据目录直接启动后同样会自动升级。4. 实战二逻辑迁移升级mysqldump / MySQL Shell4.1 导出阶段要注意的参数逻辑迁移适合想借升级机会搭一套全新环境的场景。我先说导出再讲导入最后说校验。导出阶段我推荐直接上MySQL Shell的util.dumpInstance它比mysqldump快很多而且是8.0官方推荐的逻辑备份工具开启并行后能把多张表的导出任务同时跑起来。mysqlsh rootlocalhost:3306 --util dump-instance /backup/mysql_dump --ocn参数--ocn是--consistent的简写保证一致性快照。不指定的话导出期间新写入的数据可能导致备份集不一致。如果你还是习惯用mysqldump输出要加--routines --triggers --events这几个选项前面提过。另外5.7的mysqldump导出的文件在8.0导入时基本都能兼容因为官方在这条升级路径上做了大量兼容工作。导出完成后确认一下备份文件大小和库里实际数据量是否对得上。我见过有人导出到一半磁盘满了还没发现结果导入的时候才发现备份文件是残缺的。4.2 导入阶段与数据校验导入前新8.0实例要做的事情和原地升级的准备工作一样确认sql_mode、清掉废弃参数、规划好字符集。特别是字符集导入前就把character_set_server设成utf8mb4别等导完再改否则可能有乱码风险。mysql -uroot -p /backup/mysql_dump/dump.sql如果用的是MySQL Shell导出的目录备份导入用mysqlsh rootlocalhost:3306 --util load-dump /backup/mysql_dump导入过程可能会报字符集相关的warning不用慌先全部导入完再统一检查。导入完成后数据校验这步千万别偷懒。我用的是最直接的办法——两套实例同时跑统计SQL对比-- 旧实例 SELECT table_schema, COUNT(*) FROM information_schema.tables GROUP BY table_schema; -- 新实例 SELECT table_schema, COUNT(*) FROM information_schema.tables GROUP BY table_schema;然后把几个核心大表的行数、max(id)、sum(某个关键字段)都拉出来对比一遍。如果之前开了GTID还可以用GTID集合来验证数据是否完整。5.7和8.0的GTID格式是一样的基本可以直接对照。在实践里逻辑迁移是最稳妥的方式因为导入之前可以检查所有对象的定义有问题早发现早修复。缺点就是慢。如果你的库在500GB以上我不建议用mysqldump逻辑迁移直接用原地升级更快。5. 升级后的验证、参数调整与常见报错5.1 升级完成后的系统健康检查升级完成不代表事情结束真正的工作——验证和适配——才刚开始。我总结了一套升级后的体检清单按顺序做完基本能确认实例状态正常第一确认版本和状态SELECT VERSION(); -- 期望输出 8.0.x SHOW STATUS LIKE uptime; SHOW VARIABLES LIKE innodb_version;第二检查所有库表是否正常重点关注是否有表被标记为in use或corrupted。8.0里可以用CHECK TABLE your_table_name;第三验证账号和权限。8.0升级后权限表结构变了mysql.user里新增了多个字段比如authentication_policy相关的列。如果升级前用的是mysql_native_password插件升级后连接可能报错。当前主流客户端如最新的Navicat、MySQL Workbench、各种语言的驱动都已经支持caching_sha2_password但老版本驱动的确会有问题-- 查看现有账号的认证插件 SELECT user, host, plugin FROM mysql.user; -- 如果有需要兼容的旧客户端可以改回native_password ALTER USER app_user% IDENTIFIED WITH mysql_native_password BY 你的密码;但我强烈不建议把新版本默认的caching_sha2_password全部改回mysql_native_password因为那意味着你放弃了8.0安全性升级的核心优势。正确做法是升级客户端驱动而不是降低数据库的安全标准。第四确认计划任务、存储过程和触发器状态。8.0升级后存储过程的DEFINER如果指向已删除用户调用时会报错。检查一下information_schema.routines里的DEFINER字段确保对应的账号都存在。5.2 新版本适配调整认证插件、sql_mode、缓存参数升级后有几个参数和配置需要根据业务实际调整。第一个是sql_mode。虽然升级前让你提前改成8.0默认模式跑几天但部分老业务可能仍然不兼容。比如ONLY_FULL_GROUP_BY模式下SELECT a, COUNT(*) FROM t GROUP BY b这种写法会直接报错。遇到这种问题在升级初期可以临时把ONLY_FULL_GROUP_BY去掉但心里要明白这只是过渡方案长期必须改SQL。# my.cnf 临时兼容 sql_mode STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION第二个是内存参数。8.0对缓冲池的利用效率比5.7高可以适当调大innodb_buffer_pool_size。如果服务器内存足够把它设置到物理内存的60%-70%都没问题。但注意innodb_buffer_pool_instances在8.0里默认值随buffer pool大小自动调整不再需要手动指定。另一个容易忽略的参数是innodb_redo_log_capacity。8.0里替代了5.7的innodb_log_file_size默认是100MB但对写入量大的业务来说可以调大。我一般设置成1G或2G减少日志文件切换频繁带来的性能抖动。innodb_redo_log_capacity 1G第三个是字符集。8.0默认utf8mb4如果你的库表和连接还停留在utf8mb3或latin1建议在升级后的稳定期逐步迁移到utf8mb4尤其是存表情符号或者其他多字节字符的场景。注意从utf8mb3迁移到utf8mb4会涉及表重建大表执行ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4时会有DDL锁建议用pt-online-schema-change这类工具在低峰期做。5.3 常见报错与排查速查表升级后最常碰到的报错我整理成了一个速查表按报错信息、原因、解决办法三列排实操中直接搜报错就能定位报错信息原因解决方法Unknown system variable query_cache_type配置文件残留5.7参数注释掉query_cache_type和query_cache_sizeAuthentication plugin caching_sha2_password cannot be loaded客户端驱动版本太老升级驱动或临时改账号认证插件为mysql_native_passwordERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clauseONLY_FULL_GROUP_BY启用的严格检查修改SQL或临时调整sql_modeColumn count of mysql.user is wrong. Expected X, found Y权限表未正确升级确保首次用8.0启动时完整执行升级流程不要中途kill进程Table mysql.proc doesnt exist存储过程元数据迁移问题用mysql_upgrade重新升级存储过程或重建对应存储过程[ERROR] InnoDB: Table xxx in data dictionary has mismatch数据字典和表文件不一致用ALTER TABLE xxx FORCE重建表必要时用备份恢复ERROR 1064 (42000): You have an error in your SQL syntax且提示rank、groups等词8.0新增保留字给字段名加反引号或修改字段名这里面有个非常容易踩的坑8.0新增了一批保留字比如rank、dense_rank、percent_rank、cume_dist、groups、json_table等这些是窗口函数和JSON函数带来的。如果5.7的库表里恰好有叫rank的字段升级后普通SELECT都能跑但一执行某些涉及特定语法的语句就会语法错乱。排查起来非常头疼。所以升级前的兼容性检查一定要认真看输出里面会列出这类字段。还有个小问题是关于TIMESTAMP列的。8.0里对TIMESTAMP的默认值处理更严格如果之前建表用了TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP升级后一般没问题。但如果用过DEFAULT 0000-00-00 00:00:00这种值在严格模式开启时会报错。解决办法是对这列做ALTER TABLE修改默认值或者在连接会话里临时关闭NO_ZERO_DATE。6. 最后再分享一点个人体会这次升级让我最深刻的感受是MySQL 8.0的升级难点不在“升级”这个动作本身而在升级前的评估和升级后的适配。原地的二进制替换只要二十分钟但为了让这二十分钟不出岔子我花了将近两天做兼容性检查、参数调整、备份和演练。尤其是sql_mode的调整我提前两周就在测试环境跑了一遍生产的所有核心SQL抓到两个因为ONLY_FULL_GROUP_BY导致的慢查询提前让开发重写了。另外一个小建议是升级完成后至少观察一到两周再清理旧版本的备份目录。期间如果出现任何疑难问题至少还有一条回滚的路可以走。虽然8.0的数据字典升级后理论上是不能回滚的但如果你在升级后短时间内发现重大问题用逻辑备份重建5.7实例再恢复数据还是来得及的。MySQL 8.0性能确实比5.7强很多尤其是并发写入和复杂查询场景我这边升级后有几个之前要几百毫秒的统计SQL现在几十毫秒就跑完了。升级这件事值得做而且值得认真做。
返回列表