MySQL 主从切换实现零停机升级
MySQL 本身不支持原地热升级(比如从 5.7 直接升到 8.0),所谓“无停机时间升级”,本质是靠架构切换而非单实例就地升级。最成熟、可控的方式是构建主从复制拓扑,将新版本实例作为从库拉取旧版本主库的 binlog,待数据追平后执行角色切换。
关键前提是:新旧版本间必须满足
replication compatibility。例如: 5.7 → 8.0 支持(但需关闭
sql_mode中的
NO_AUTO_CREATE_USER等已移除项) 8.0 → 5.7 不支持(高版本 binlog event 类型低版本无法解析) 跨大版本时,
mysql_upgrade不可用于在线实例,只能在从库停写后执行(且仅限同版本升级场景)
使用 MySQL Shell 的 AdminAPI 自动化滚动升级
MySQL 8.0.19+ 内置的
AdminAPI(通过
mysqlsh)可管理 InnoDB Cluster,支持对集群节点逐个升级而不中断服务。它底层仍是主从切换逻辑,但封装了状态检查、故障隔离、自动重试等细节。
实操要点:
确保所有节点启用group_replication且运行在兼容模式(如
group_replication_consistency=AFTER) 升级前用
cluster.checkInstanceConfiguration()验证目标实例配置(尤其
binlog_format=ROW、
enforce_gtid_consistency=ON) 执行
cluster.upgradeMetadata()仅更新元数据版本,不触碰数据;真正的二进制升级需先停掉该节点的
mysqld,替换二进制,再以
--upgrade=MINIMAL启动
pt-online-schema-change 不能用于版本升级
有人误以为
pt-online-schema-change或
gh-ost能做版本升级——它们只解决「表结构变更时避免锁表」的问题,和 MySQL 服务进程的二进制替换、系统表迁移、字符集默认行为变更等完全无关。
典型误用后果:
在 5.7 主库上用gh-ost迁移一张表,再把该表所在的实例升级到 8.0 →
mysql.user表结构已变,权限系统直接失效 跳过
mysql_upgrade步骤直接启动 8.0 二进制读取 5.7 数据目录 → 启动失败并报错
Table 'mysql.role_edges' doesn't exist未重放完所有 binlog 就切换流量 → 出现主键冲突或丢失事务
升级后必须验证的三个隐性断裂点
即使切换成功,以下三点极易被忽略,导致后续业务异常:
TIMEZONE行为差异:5.7 默认用系统时区,8.0 默认用
SYSTEM但首次启动会尝试写入
mysql.time_zone表;若未加载时区表,
NOW()和
CONVERT_TZ()结果错乱
lower_case_table_names默认值变化:Linux 下 5.7 默认为 0,8.0 默认为 1 —— 若应用硬编码了大小写混用的表名,查询会突然返回空结果
default_authentication_plugin:8.0 默认改用
caching_sha2_password,老客户端(如 MySQL 5.6 JDBC driver)连接会报错
Unknown authentication plugin caching_sha2_password
这些不是“升级没完成”,而是“升级完成了但环境没对齐”。上线前必须用真实业务 SQL 在新实例上回放验证,不能只看
SHOW SLAVE STATUS\G的
Seconds_Behind_Master: 0。
