在生产环境中,MySQL 数据库升级是一项高风险操作,尤其是在主主(Master-Master)架构配合 Keepalived 实现高可用的场景下。任何操作失误都可能导致业务中断或数据不一致。
本文基于多次生产环境升级实战经验(8.0.23→8.0.43、5.7.18→8.0.44 等),总结出一套标准化的平滑升级方案,可作为后续升级的通用指导。
一、升级策略概述#
1.1 核心思路#
采用**“双实例并行 + 滚动切换”**策略:
- 并行部署:在新版本(或新服务器)上部署 3307 实例,与线上 3306 建立同步
- 数据预热:提前完成全量数据同步,缩短变更窗口
- 滚动切换:逐台替换,VIP 漂移保证业务连续性
- 版本回退:保留旧实例,出问题可快速回滚
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)#
变更中#
升级后(D+1)#
八、总结#
生产环境 MySQL 主主+Keepalived 升级的核心要点:
- 充分预准备:数据预热、环境检查、脚本验证
- 小步快跑:分阶段执行,每步验证后再继续
- 快速回滚:保留旧实例,确保出问题可立即恢复
- 监控护航:变更期间密切观察同步状态和业务指标
通过标准化流程,可将升级风险降至最低,确保业务连续性。
参考案例:
- 业务 A 数据库:8.0.23 → 8.0.43(原地升级)
- 业务 B 数据库:5.7.18 → 8.0.44(新服务器迁移)
- 业务 C 数据库:8.0.23 → 8.0.44(原地升级+GTID 开启)
本文基于真实生产环境升级经验整理,如有问题欢迎留言讨论。