乌班图 MySQL 小版本升级全攻略:备份、回滚与主从实践 1. 先弄清楚你所说的小版本升级到底是哪种升级前阵子收到安全公告说我那台乌班图Ubuntu22.04 上的 MySQL 8.0.34 需要升级到 8.0.36 以上当时我的第一反应是这不就是 apt 换一下版本号的事吗可真要动手才发现乌班图 MySQL 小版本升级这件事说简单也简单说坑也是真不少。一台跑了三年多的 8.0.35数据目录、慢查询配置、监控脚本都稳定地耦合在一起我不能只改一个版本号就完事。这篇文章就围绕“乌班图 MySQL 小版本升级”这个具体场景把版本确认、备份回滚、三条主流升级路径、执行时的坑、主从环境顺序、以及升级后的验证全部讲清楚。适合自己买了服务器、或者在公司内网跑 MySQL 的朋友尤其是那种担心升级丢数据、服务起不来的读者。1.1 先分清小版本升级和大版本升级是两码事MySQL 的版本号是三段式X.Y.ZX 是主版本号Y 是发布系列号Z 是补丁版本号。举个例子8.0.36 就是 8.0 系列的第 36 个补丁版。我们通常说的“小版本升级”指的是同一个 Y 之下 Z 的变化比如 8.0.35 升到 8.0.36或者 5.7.35 升到 5.7.44。而“5.7 升到 8.0”这种属于大版本升级执行逻辑和风险完全不在一个档次。我见过不少朋友把两件事混在一起拿 5.7 升 8.0 的教程来给 8.0.35 升 8.0.36 用结果中途卡在数据字典检查流程上折腾半天才发现方向不对。小版本升级的核心是数据字典结构基本不变主要替换的是二进制程序所以理论上比大版本升级安全得多。但也正因为“看起来简单”很多人会跳过备份、忽略环境验证真出了问题才发现自己连回滚的底气都没有。1.2 乌班图上 MySQL 的三种安装来源对应不同升级方式升级方式高度依赖当初你是“怎么装上来”的。我梳理过最常见的三种安装来源升级方式差别很大安装来源典型部署方式升级本质最大风险点apt 仓库安装系统自带或官方仓库apt install mysql-server通过 apt/dpkg 替换二进制包源优先级、版本被 hold、依赖冲突离线 deb 包安装dpkg -i xxx.deb手动替换二进制包依赖缺失、安装顺序错误官方 tar.gz 二进制包部署解压到/usr/local/mysql手动替换程序目录权限不一致、数据目录被误覆盖我实际工作中遇到最多的是第一种服务器用 apt 装好 MySQL然后日常维护都在 apt 体系内跑。内网环境则大量用第二种离线 deb。第三种一般出现自运维风格比较“硬核”的朋友手里。下面三条路径都会给到但重点会放在第一、第二种上。1.3 升级前先花三分钟看清现状不管你最终选哪条路径动手之前先确认现状。正常情况下用下面这几条命令就能拿到关键信息mysql --version mysql -u root -p -e SELECT VERSION(); SHOW VARIABLES LIKE datadir; dpkg -l | grep mysql-server如果现在服务已经挂了连不上 MySQL也可以通过dpkg -l | grep mysql来确认已安装包的版本或者直接看/var/log/mysql/error.log顶部的版本标识。另外乌班图的/etc/mysql/目录下会有多个配置片段升级后一些配置项可能被新版本废弃或改名所以我通常会先把整个/etc/mysql/目录备份一份。这个操作成本极低但收益很大——万一升级后启动参数不兼容你至少能快速对比出差异。2. 备份和回滚预案小版本升级前必做的三件事2.1 别以为小版本升级就不需要备份有些朋友会说小版本升级又不改数据文件为什么非要备份实际上升级过程中磁盘写满、中途断电、新二进制和现有数据文件格式有细微不兼容这些情况在乌班图上我都见过。InnoDB 的数据字典一般不会动但意外一旦发生没有备份就只能靠 binlog 追溯而 binlog 没有开启的话就是灾难。我处理过一个真实案例某台内网服务器做 8.0.35 到 8.0.36 升级时因为/var/lib/mysql所在分区剩余空间不足MySQL 启动直接失败日志里全是磁盘写入错误。幸好那哥们之前做了一个物理冷备最后用备份恢复半小时内就恢复了业务。所以我的建议是至少保留一份物理备份有条件再做一份逻辑备份。逻辑备份最常用的是mysqldumpmysqldump -u root -p \ --single-transaction --quick \ --routines --events --triggers \ --all-databases alldb_$(date %F).sql--single-transaction对 InnoDB 表比较友好备份过程中不会长时间锁表但 MyISAM 表仍然可能被短暂锁定。逻辑备份的好处是 SQL 文本可读、可筛选坏处是恢复慢数据量大时可能要跑很久。物理冷备也很直观systemctl stop mysql tar -czf /backup/mysql_datadir_$(date %F).tar.gz /var/lib/mysql systemctl start mysql这种备份恢复最快升级翻车直接把 tar 包解回去就好。如果你用的是 MySQL 8.0.17 以上版本还可以考虑 Clone Plugin 做在线克隆不影响源实例但配置起来比前两种稍复杂一点。2.2 回滚判断标准能不能原地降级小版本升级之后能不能降级绝大多数 8.0.x 之间的补丁版可以原地降级前提是数据字典没有被新版修改。但 8.0 早期版本和后期版本之间并不绝对5.7 升到 8.0 之后基本上不能直接降回 5.7。所以我有一个习惯升之前把旧版本的 deb 包或官方 tar.gz 包也下载保存一份万一新版本启动失败直接换回旧程序配合物理备份恢复这样心里才踏实。2.3 记录环境快照避免升级后两眼一抹黑升级前花两分钟把环境信息记下来不用写文档自己存个备忘录就行。重点包括datadir 路径通常默认是/var/lib/mysql配置文件路径乌班图上主要是/etc/mysql/my.cnf和/etc/mysql/mysql.conf.d/mysqld.cnf端口和 socket 路径默认是3306和/var/run/mysqld/mysqld.sock启动用户和目录权限一般是mysql:mysql升级后如果连不上第一件事就是拿这些信息做对比而不是盲目改配置。我遇到过不少报 2003 连接错误的情况最后发现就是 socket 路径变了或者 bind-address 配置被新版本调整过。3. 三条典型升级路径从源锁定到离线包按场景选3.1 路径 A官方 APT 仓库精确锁定小版本如果你的乌班图能访问公网推荐用 MySQL 官方 APT 仓库升级这是最省事的路径。先添加官方仓库wget https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb sudo dpkg -i mysql-apt-config_0.8.29-1_all.deb sudo apt update这个配置包的版本号以后可能会更新具体以官网下载页为准。安装过程中会弹窗问你要选哪个 MySQL 版本选择 8.0 保存即可。然后查看当前可用的 MySQL 版本apt-cache policy mysql-server apt list -a mysql-server你会看到一系列候选版本包括8.0.36-1ubuntu22.04之类的版本号。精确安装目标版本sudo apt-get install mysql-server8.0.36-1ubuntu22.04注意版本号格式是“上游版本号-1ubuntu系列”具体以apt-cache policy输出为准。如果你之前装的是乌班图系统源里的 mysql-server切换到官方仓库后 apt 会自动按官方仓库的版本解析。升级完成后重启服务sudo systemctl restart mysql这里说明一下为什么建议用等号精确指定版本而不是直接apt-get upgrade。在生产环境里直接 upgrade 可能会把 MySQL 大版本也一并带上去或者装了还没验证过的补丁版。指定版本号能保证你只做“小版本升级”不会顺手把其他系统组件也动一遍。3.2 路径 B内网离线环境用 deb 包升级内网机器连不了外网的场景很常见。去 MySQL 官方下载页面把mysql-server、mysql-client、mysql-common、libmysqlclient*这几个 deb 包下载齐放到同一目录下。然后有两种安装方式推荐用apt安装本地包它会自动处理依赖sudo apt install ./mysql-server_8.0.36-1ubuntu22.04_amd64.deb如果系统提示依赖缺失先执行sudo apt-get -f install修复依赖后再继续。旧式做法是直接用dpkg手动安装但我建议安装顺序按 common、client、server 来因为mysql-server对前两者有硬依赖sudo dpkg -i mysql-common_*.deb mysql-client_*.deb mysql-server_*.deb直接先敲dpkg -i mysql-server很容易提示缺依赖然后就得再回头补齐。这个坑我在离线环境里踩过好几次顺序对了能省十分钟。3.3 路径 C官方 tar.gz 二进制包原地替换当初用官方二进制包手动部署到/usr/local/mysql的朋友做小版本升级也不复杂核心思路是停服务、备份旧程序目录、替换 bin 和 lib、保留 datadir 和配置。完整命令systemctl stop mysql mv /usr/local/mysql /usr/local/mysql.bak_$(date %F) tar -xf mysql-8.0.36-linux-glibc2.17-x86_64-minimal.tar.xz mv mysql-8.0.36-linux-glibc2.17-x86_64 /usr/local/mysql chown -R mysql:mysql /usr/local/mysql systemctl start mysql有两个坑必须提醒。第一不要把整个/usr/local/mysql目录rm -rf再解压新的那样很容易连带删掉你自定义的路径配置、软链接和自定义插件。第二替换完记得确认文件权限我见过一次替换后mysqld文件变成了 644启动直接报 Permission denied最后chmod 755 /usr/local/mysql/bin/mysqld才解决。在乌班图上权限问题是手动部署最常见的问题源没有之一。4. 乌班图执行升级时的几个魔鬼细节4.1 用 apt-mark hold 防止被系统更新顺手带走如果你平时会执行apt upgrade升级完 MySQL 后一定要考虑把它 hold 住否则哪天系统更新就把 MySQL 版本一起带跑了既可能跳过你精心验证过的小版本也可能引入新问题。sudo apt-mark hold mysql-server mysql-client mysql-common想恢复自动升级时再 unholdsudo apt-mark unhold mysql-server mysql-client mysql-common这个操作不影响你手动做小版本升级只是让你对版本有控制权。生产环境我基本都会建议 hold 住等正式评估过再手动升。4.2 systemd 单元文件和服务脚本混乱乌班图 22.04 的 MySQL 8.0 一般都用 systemd 管理systemctl restart mysql是很常规的操作。但如果这台机器是从 MySQL 5.7 一路升上来的老机器可能还残留/etc/init.d/mysql这类 SysV 脚本升级后服务启停有可能出现混乱。遇到服务启动失败别只盯着systemctl status mysql前面的几行要看完整日志journalctl -u mysql -n 100我遇到过升级后重启失败日志里报Access denied for user debian-sys-maint。这个用户是乌班图 mysql-server 包内部用来做日常维护的特殊账号密码记录在/etc/mysql/debian.cnf里。升级后配置文件和 data 目录里实际账号不一致就会反复报这个错。解决方法是核对/etc/mysql/debian.cnf里的账号和权限是否和数据目录一致确认后重启服务。这是乌班图特有、但在网上很少被提及的经典坑。4.3 mysql_upgrade 到底什么时候跑千万别乱跑关于mysql_upgrade很多旧教程还在讲“升级完必须手动跑 mysql_upgrade”但这里必须分清楚版本。MySQL 8.0.16 开始mysqld 启动时会自动执行升级检查不需要手动执行 mysql_upgrade 了。如果你拿旧教程对着 8.0.36 手动跑运气好是多此一举运气不好可能触发版本不匹配的报错。如果你真的必须手动执行比如从 8.0.15 或更早版本升级到新版本请确保使用和当前二进制一致的mysql_upgrade程序mysql_upgrade -u root -p而 8.0.16 之后的正常流程是升级包之后第一次启动 mysqld看 error log 里有没有类似Checking if update is needed的记录这条记录说明自动升级流程已经启动。如果看到Upgrade process encounters error必须立刻停下来查看完整日志不要继续操作。4.4 连接不上的典型报警error 2003 到底怎么查升级后最常收到的报警就是ERROR 2003 (HY000): Cant connect to MySQL server on localhost:3306 (10061)。严格来说这个错误表示客户端根本没有连到 MySQL 服务可能是服务没启动、端口没监听、防火墙拦截或 socket 路径不一致。排查的黄金组合命令systemctl status mysql ss -tlnp | grep 3306 mysql -u root -p -h 127.0.0.1 -P 3306 mysql -u root -p -S /var/run/mysqld/mysqld.sock如果 3306 端口在监听但 TCP 连不上检查/etc/mysql/mysql.conf.d/mysqld.cnf里的bind-address和skip-networking配置。如果 socket 方式能连而 TCP 不能连通常也是这两个配置项的问题。另外 MySQL 8.0 默认 root 账号使用的是caching_sha2_password认证插件老版本客户端连接时可能会报unknown plugin caching_sha2_password这时需要升级客户端版本或者调整账号的认证插件。升级后连接相关问题按这个顺序排查基本能覆盖 90% 的情况。4.5 依赖库缺失libaio1 和 libnuma1 不能少在比较精简的乌班图上装 MySQL 8.0很容易缺libaio1、libnuma1这些运行库离线 deb 包升级时尤其常见。如果升级后 mysqld 启动失败并且 error log 里提到缺少共享库先把运行库补齐sudo apt-get install -y libaio1 libnuma1 libtinfo5装完再启动服务。这个坑很小但排查起来很烦人经常被人忽略。5. 主从/复制环境的小版本升级顺序和执行窗口5.1 为什么一定要先升从库生产环境很少是单机主从复制下的小版本升级讲究顺序。我的原则永远是先升从库观察没问题后再升主库。如果从库升级后启动失败主库仍然在跑旧版本业务不受影响回滚范围也被限制在从库。如果反过来先升主库一旦新版本有问题整个写入路径全挂在奇怪状态上恢复成本直接翻倍。5.2 升级前检查复制状态别带延迟升级动手升级之前在主库上检查复制状态SHOW REPLICA STATUS\G注意MySQL 8.0.22 之后命令从SHOW SLAVE STATUS改成了SHOW REPLICA STATUS。主要观察两个字段Slave_IO_Running和Slave_SQL_Running是否都为YesSeconds_Behind_Master是否为0。如果从库还有大量延迟我会先等它追平再做升级避免在升级过程中出现复制通讯异常排查起来特别麻烦。5.3 一个真实的升级报警Error reading packet from server有一次我把从库从 8.0.35 升到 8.0.36从库日志里出现了Error reading packet from server的报错。我一开始以为是补丁版本之间的协议差异排查了很久最后发现是主库在维护窗口里恰好有大事务 DDLbinlog 传输时旧连接断开重连产生了瞬时告警。小版本升级时这种问题不常见但遇到也别慌通常重连就能恢复。我的建议是升级窗口内尽量不要同时跑大事务、大 DDL 或大批量写入任务。5.4 完整执行顺序参考如果条件允许我一般按这个顺序操作从库升级停服务、替换包、重启服务、看 error log。从库验证通过后持续观察一段时间确认无复制报错。主库升级前先把业务写入降级或设置只读SET GLOBAL read_only ON;在主库做一次FLUSH TABLES WITH READ LOCK;可选再做一次物理冷备。按同样步骤升级主库并重启。恢复read_only OFF观察主从状态是否恢复正常。如果主库升级失败优先用物理备份恢复或者把旧二进制替换回去千万不要试图去修改从库数据来匹配主库。那种操作一旦走错整条复制链路都会废掉。6. 升级后的验证清单与我最想提醒的一件事6.1 验证版本和运行状态升级完成不代表结束验证才是重点。先确认版本号和运行状态mysql -u root -p -e SELECT VERSION(); SHOW STATUS LIKE Uptime; SHOW VARIABLES LIKE innodb_version;同时去看/var/log/mysql/error.log确认日志末尾有ready for connections。这个日志是 MySQL 真正启动完成最可靠的标志。另外确认数据目录权限ls -ld /var/lib/mysql df -h /var/lib/mysql如果权限意外变成 rootmysqld 会拒绝读取数据目录启动即失败。修正时记得先停服务再执行chown -R mysql:mysql /var/lib/mysql然后启动。6.2 数据完整性抽查与性能回检升级前如果记录过关键表的 checksum升级后直接对比最靠谱CHECKSUM TABLE db1.t_order, db2.t_user;没有记录 checksum 也没关系抽几张关键大表做SELECT COUNT(*)再随机抽样看几条数据是否正常。性能方面观察慢查询日志有没有明显变慢redo log 文件大小和 buffer pool 命中率有没有异常波动。小版本升级通常性能差异很小如果出现明显的锁等待或大量慢查询优先查看版本 release notes 里有没有已知问题。6.3 我最想提醒的一件事小版本升级不是越新越好最后说一个很多人容易忽略的点补丁版本不是越新越好。每次 MySQL 发布新补丁版社区都会在短时间内反馈一些新问题某些 8.0.x 在特定并发场景下表现反而不如旧版的现象也出现过。我的习惯是让目标版本先“飞”一段时间等两到四周没有大规模负面反馈再升级。另外别在乌班图系统源里同时混装 mysql-5.7 和 mysql-8.0 的仓库apt 有时候会把 mysql-common 解析成另一个系列导致版本互相覆盖。我见过一次升级后 mysql-common 被莫名替换成 5.7整个包管理依赖一团乱最后只能手动卸载重装。小版本升级虽然动作小但前面说的三件事确认版本来源、做备份、记录环境快照只要扎实做好了剩下交给重启后的日志验证就行。服务器这东西稳比新重要。