前言

在生产环境中,MySQL 数据库升级是一项高风险操作,尤其是在主主(Master-Master)架构配合 Keepalived 实现高可用的场景下。任何操作失误都可能导致业务中断或数据不一致。

本文基于多次生产环境升级实战经验(8.0.23→8.0.43、5.7.18→8.0.44 等),总结出一套标准化的平滑升级方案,可作为后续升级的通用指导。


一、升级策略概述

1.1 核心思路

采用**“双实例并行 + 滚动切换”**策略:

  1. 并行部署:在新版本(或新服务器)上部署 3307 实例,与线上 3306 建立同步
  2. 数据预热:提前完成全量数据同步,缩短变更窗口
  3. 滚动切换:逐台替换,VIP 漂移保证业务连续性
  4. 版本回退:保留旧实例,出问题可快速回滚

1.2 适用场景

场景说明
原地升级同一台服务器,高版本并行部署在 3307,完成后切换端口
迁移升级新服务器部署,通过主从同步迁移数据,VIP 切换流量

二、升级前准备(Pre-check)

2.1 环境检查清单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 检查当前版本
mysql -e "SELECT VERSION();"

# 2. 确认 GTID 状态(8.0 必须开启)
mysql -e "SHOW VARIABLES LIKE 'gtid_mode';"
mysql -e "SHOW VARIABLES LIKE 'enforce_gtid_consistency';"

# 3. 检查主从同步状态
mysql -e "SHOW MASTER STATUS;"
mysql -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master"

# 4. 检查数据一致性(percona-toolkit)
pt-table-checksum --host=DB1 --user=checksum_user --password=xxx --databases=db1,db2

# 5. 检查账号权限
mysql -e "SELECT user, host, plugin FROM mysql.user;"

# 6. 检查磁盘空间
df -h

# 7. 检查 Keepalived 配置
systemctl status keepalived
cat /etc/keepalived/keepalived.conf

2.2 预升级部署(以原地升级为例)

DB1 / DB2 同时执行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 1. 安装新版本 MySQL 8.0.XX
# 2. 初始化 3307 实例
mysqld --defaults-file=/etc/mysql/my3307.cnf --initialize-insecure

# 3. 启动 3307 实例
systemctl start mysql3307

# 4. 配置主从同步(3307 作为 3306 的从库)
# DB2 的 3307 指向 DB1 的 3306
# DB1 的 3307 指向 DB2 的 3306(或延后配置)

关键配置文件 /etc/mysql/my3307.cnf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[mysqld]
port = 3307
datadir = /data/mysql3307
server-id = 101  # DB2 改为 102

gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = mysql-bin
binlog_format = ROW

# 8.0 默认认证插件
default_authentication_plugin = mysql_native_password
# 或 caching_sha2_password(根据业务兼容性选择)

2.3 数据预同步验证

1
2
3
4
5
6
7
8
9
# 检查同步延迟
mysql -P3307 -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master

# 全量数据校验(升级前一天执行)
pt-table-checksum --host=DB1 --port=3306 --user=pt_user \
  --host=DB2 --port=3307 --databases=all

# 修复不一致数据
pt-table-sync --execute h=DB1,P=3306,h=DB2,P=3307 --databases=db1

三、正式变更步骤

阶段一:切换第一台(DB2 升级)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1. 拆除原双主关系
mysql -P3306 -e "STOP SLAVE; RESET SLAVE ALL;"  # DB1 上执行,停止指向 DB2 的同步

# 2. 停止 DB2 的 3306 老实例
systemctl stop mysql3306

# 3. 修改 DB2 的 3307 配置,端口改为 3306
sed -i 's/port = 3307/port = 3306/' /etc/mysql/my3307.cnf
systemctl restart mysql3307
# 重命名为 mysql3306 服务(可选)

# 4. 重建双主(DB2 新实例 + DB1 老实例)
# DB2 上执行:
mysql -e "CHANGE MASTER TO MASTER_HOST='DB1', MASTER_PORT=3306, \
  MASTER_USER='repl', MASTER_PASSWORD='xxx', \
  MASTER_AUTO_POSITION=1;"
mysql -e "START SLAVE;"

# DB1 上执行:
mysql -e "CHANGE MASTER TO MASTER_HOST='DB2', MASTER_PORT=3306, \
  MASTER_USER='repl', MASTER_PASSWORD='xxx', \
  MASTER_AUTO_POSITION=1;"
mysql -e "START SLAVE;"

# 5. 检查主主状态
mysql -e "SHOW SLAVE STATUS\G" | grep -E "Slave_IO_Running|Slave_SQL_Running"

阶段二:VIP 漂移(流量切换)

1
2
3
4
5
6
7
8
# 1. 停止老主(DB1)Keepalived
systemctl stop keepalived

# 2. VIP 自动飘到 DB2,检查
ip addr | grep VIP_IP

# 3. 验证业务连接
mysql -hVIP_IP -e "SELECT @@hostname, VERSION();"

阶段三:切换第二台(DB1 升级)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 拆除临时双主
mysql -P3306 -e "STOP SLAVE; RESET SLAVE ALL;"  # DB2 上执行

# 2. 停止 DB1 的 3306 老实例
systemctl stop mysql3306

# 3. 修改 DB1 的 3307 配置,端口改为 3306
sed -i 's/port = 3307/port = 3306/' /etc/mysql/my3307.cnf
systemctl restart mysql3307

# 4. 重建双主(两台都是新实例)
# DB1 上执行:
mysql -e "CHANGE MASTER TO MASTER_HOST='DB2', MASTER_PORT=3306, \
  MASTER_USER='repl', MASTER_PASSWORD='xxx', \
  MASTER_AUTO_POSITION=1;"
mysql -e "START SLAVE;"

# DB2 上执行:
mysql -e "CHANGE MASTER TO MASTER_HOST='DB1', MASTER_PORT=3306, \
  MASTER_USER='repl', MASTER_PASSWORD='xxx', \
  MASTER_AUTO_POSITION=1;"
mysql -e "START SLAVE;"

阶段四:VIP 回切(恢复原始拓扑)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 1. 重启 DB1 Keepalived(此时 VIP 会在两台同时存在,需手动处理)
systemctl start keepalived

# 2. 手动在 DB1 添加 VIP(避免脑裂)
ip addr add VIP_IP/24 dev eth0

# 3. 停止 DB2 Keepalived,让 VIP 飘回 DB1
systemctl stop keepalived

# 4. 检查 VIP
ip addr | grep VIP_IP  # 确认在 DB1 上

# 5. 重新启动 DB2 Keepalived
systemctl start keepalived

阶段五:最终检查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1. Keepalived 状态
systemctl status keepalived
cat /var/log/messages | grep -i keepalived

# 2. 主主同步状态
mysql -e "SHOW SLAVE STATUS\G" | grep -E "Running|Behind"

# 3. 应用连接检查
ss -tnp | grep :3306
mysql -e "SHOW PROCESSLIST;" | grep -c "Connect"

# 4. 监控告警检查
# 确认所有监控项正常

四、典型问题与规避方案

问题 1:数据预检查缺失

现象:升级后发现数据不一致,需要大量时间修复。

规避方案

1
2
3
4
5
# 升级前 24 小时执行数据校验
pt-table-checksum --databases=all --replicate=percona.checksums

# 如有差异,提前修复
pt-table-sync --execute h=source,h=dest --databases=db1

问题 2:GTID 未开启

现象:MySQL 8.0 要求 GTID,但旧实例未开启,导致同步失败。

规避方案

1
2
3
4
5
-- 升级前检查
SELECT @@gtid_mode, @@enforce_gtid_consistency;

-- 如未开启,需在升级前.plan 中规划 GTID 开启步骤
-- 注意:开启 GTID 需要重启 MySQL

问题 3:Keepalived 检测脚本失败

现象:MySQL 实际正常,但 Keepalived 认为异常,导致 VIP 频繁漂移。

规避方案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 检查脚本路径是否正确
which mysql
ls -la /usr/local/mysql/bin/mysql

# 检测脚本示例(使用绝对路径)
cat /etc/keepalived/check_mysql.sh
#!/bin/bash
export MYSQL_PWD='password'
/usr/local/mysql/bin/mysql -h127.0.0.1 -P3306 -e "SELECT 1;" > /dev/null 2>&1
if [ $? -ne 0 ]; then
    systemctl stop keepalived
    exit 1
fi
exit 0

问题 4:账号权限问题

现象:升级后应用无法连接,或复制用户权限不足。

规避方案

1
2
3
4
5
6
7
8
-- 检查认证插件
SELECT user, host, plugin FROM mysql.user;

-- 如应用使用旧驱动,可能需要保留 mysql_native_password
ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

-- 复制用户权限
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';

问题 5:安全漏洞

现象:升级后扫描仍有 CVE 漏洞。

规避方案

  • 升级到最新补丁版本(如 8.0.44 而非 8.0.30)
  • 升级前查询 Oracle CPU 公告,选择已修复已知漏洞的版本
  • 升级后立即执行安全加固脚本

五、新服务器迁移升级模式

当需要更换硬件或操作系统时,采用新服务器迁移模式:

流程对比

步骤原地升级新服务器迁移
预部署3307 实例新机器部署 3306
数据同步本地多实例跨网络主从同步
切换方式端口调整VIP 指向新机器
回滚启动旧实例切回旧机器 VIP

关键步骤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 1. 新服务器部署(提前完成)
# - 系统配置优化
# - MySQL 安装
# - Keepalived 配置
# - 主从同步建立

# 2. 变更窗口操作
# 下线旧 DB2 Keepalived
systemctl stop keepalived  # 旧 DB2

# 新 DB1 + 旧 DB1 组成临时集群
# VIP 切到新服务器

# 3. 验证后,新 DB1 + 新 DB2 组成最终集群
# 移除旧服务器

六、回滚预案

6.1 立即回滚(变更中出现问题)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 1. 停止新实例
systemctl stop mysql3307

# 2. 启动旧实例
systemctl start mysql3306

# 3. 恢复 Keepalived
systemctl restart keepalived

# 4. 检查 VIP 和应用

6.2 数据回滚(已写入新实例,需要回退)

1
2
3
4
5
6
7
8
# 1. 停止业务写入
# 2. 记录新实例 binlog 位置
mysql -e "SHOW MASTER STATUS;"

# 3. 反向同步(如差异较小)
# 或数据导出导入(如差异较大)

# 4. 切换回旧实例

七、变更 Checklist

升级前(D-1)

  • 全量备份(物理+逻辑)
  • 数据一致性校验通过
  • 3307 实例部署完成,同步无延迟
  • Keepalived 配置检查
  • 脚本路径验证
  • 账号权限验证
  • 业务方通知

变更中

  • 拆除双主
  • 停止老实例
  • 端口切换
  • 重建双主
  • VIP 漂移验证
  • 应用连接验证
  • 监控告警检查

升级后(D+1)

  • 性能基准对比
  • 慢查询检查
  • 错误日志检查
  • 清理旧实例(确认稳定后)

八、总结

生产环境 MySQL 主主+Keepalived 升级的核心要点:

  1. 充分预准备:数据预热、环境检查、脚本验证
  2. 小步快跑:分阶段执行,每步验证后再继续
  3. 快速回滚:保留旧实例,确保出问题可立即恢复
  4. 监控护航:变更期间密切观察同步状态和业务指标

通过标准化流程,可将升级风险降至最低,确保业务连续性。


参考案例

  • 业务 A 数据库:8.0.23 → 8.0.43(原地升级)
  • 业务 B 数据库:5.7.18 → 8.0.44(新服务器迁移)
  • 业务 C 数据库:8.0.23 → 8.0.44(原地升级+GTID 开启)

本文基于真实生产环境升级经验整理,如有问题欢迎留言讨论。