Skip to content

MySQL 升级回滚方案

概述

回滚方案的重要性

MySQL 升级是一项高风险的操作,尽管我们在升级前做了充分的准备和测试,但仍然可能遇到各种意外情况。回滚方案是升级过程中的最后一道防线,它可以在升级失败或升级后出现严重问题时,快速将系统恢复到升级前的状态,最大限度地减少业务损失。

回滚方案的目的

  • 快速恢复服务:在升级失败或出现严重问题时,快速将系统恢复到升级前的状态
  • 减少业务影响:最大限度地减少升级失败对业务的影响
  • 保障数据安全:确保回滚过程中数据的安全性和完整性
  • 降低操作风险:为升级操作提供安全保障,增强团队信心

回滚方案的原则

  • 可行性:回滚方案必须经过测试,确保在实际情况下能够成功执行
  • 完整性:回滚方案必须覆盖升级的所有方面,包括数据库、配置、应用等
  • 及时性:回滚操作必须快速执行,减少业务 downtime
  • 数据一致性:回滚过程中必须确保数据的一致性和完整性
  • 可操作性:回滚步骤必须具体、清晰、易于执行
  • 文档化:回滚方案必须详细记录在文档中,便于团队成员查阅和执行

回滚触发条件

在升级过程中或升级后,如果出现以下情况,应考虑执行回滚操作:

升级过程中触发回滚的条件

  • MySQL 服务无法启动:升级后 MySQL 服务无法正常启动,且在规定时间内无法解决
  • 数据损坏:升级过程中出现数据损坏,无法修复
  • 系统崩溃:升级过程中导致系统崩溃,无法恢复
  • 升级超时:升级过程超出预期时间,影响业务正常运行
  • 严重错误:升级过程中出现严重错误,如系统表损坏、数据丢失等

升级后触发回滚的条件

  • 应用兼容性问题:升级后应用程序无法正常工作,且在规定时间内无法解决
  • 性能严重下降:升级后系统性能严重下降,影响业务正常运行
  • 功能失效:升级后关键功能失效,无法正常使用
  • 数据不一致:升级后出现数据不一致的情况
  • 安全问题:升级后出现安全漏洞或权限问题
  • 频繁崩溃:升级后系统频繁崩溃,无法稳定运行

回滚决策流程

当出现上述情况时,应按照以下流程进行决策:

  1. 问题评估:由技术负责人组织团队对问题进行评估,确定问题的严重程度和影响范围
  2. 影响分析:分析回滚操作对业务的影响,包括回滚时间、数据损失风险等
  3. 决策制定:根据问题评估和影响分析结果,由项目负责人或更高管理层决定是否执行回滚
  4. 回滚执行:如果决定执行回滚,按照回滚方案执行回滚操作
  5. 回滚验证:回滚完成后,对系统进行验证,确保系统恢复正常
  6. 总结分析:对回滚原因、过程和结果进行总结分析,提出改进措施

回滚前准备

在执行回滚操作前,需要做以下准备工作:

确认回滚范围

  • 确认需要回滚的数据库实例
  • 确认需要回滚的版本(回滚到哪个版本)
  • 确认回滚的方式(In-place 回滚或 Logical 回滚)

备份当前状态

在执行回滚前,对当前状态进行备份,以便在回滚过程中出现问题时恢复:

  • 备份当前数据目录
  • 备份当前配置文件
  • 备份当前日志文件
  • 记录当前系统状态和关键指标

示例

bash
# 备份当前数据目录
cp -r /var/lib/mysql /var/lib/mysql.bak.rollback.$(date +%Y%m%d_%H%M%S)

# 备份当前配置文件
cp /etc/my.cnf /etc/my.cnf.bak.rollback.$(date +%Y%m%d_%H%M%S)

# 备份当前日志文件
cp -r /var/log/mysql /var/log/mysql.bak.rollback.$(date +%Y%m%d_%H%M%S)

准备回滚工具和资源

  • 准备回滚所需的二进制文件(旧版本 MySQL)
  • 准备回滚所需的工具,如 xtrabackupmysqldump
  • 确保有足够的磁盘空间用于回滚操作
  • 确保网络连接稳定

通知相关人员

  • 通知业务部门回滚计划和预计影响时间
  • 通知开发团队回滚计划
  • 通知运维团队回滚计划
  • 通知监控人员回滚计划,做好监控准备

停止写入流量

在执行回滚操作前,需要停止写入流量,确保数据一致性:

  • 通知业务部门停止写入操作
  • 或使用 read_only 参数设置数据库为只读模式

示例

sql
SET GLOBAL read_only = ON;

In-place 升级的回滚步骤

In-place 升级的回滚主要是恢复旧版本的二进制文件和数据目录。

回滚步骤

  1. 停止当前 MySQL 服务
bash
systemctl stop mysql
  1. 恢复旧版本的二进制文件

根据安装方式,恢复旧版本的二进制文件:

RPM/YUM 安装方式

bash
# 卸载当前版本
yum remove mysql-community-server mysql-community-client mysql-community-common mysql-community-libs

# 安装旧版本
yum install mysql-community-server-8.0.28 mysql-community-client-8.0.28 mysql-community-common-8.0.28 mysql-community-libs-8.0.28

APT 安装方式

bash
# 卸载当前版本
apt-get remove --purge mysql-server mysql-client mysql-common
apt-get autoremove
apt-get autoclean

# 安装旧版本
apt-get install mysql-server=8.0.28-1debian10 mysql-client=8.0.28-1debian10 mysql-common=8.0.28-1debian10

手动编译安装方式

bash
# 停止 MySQL 服务后,替换二进制文件
cp -r /usr/local/mysql.bak.8.0.28/* /usr/local/mysql/
  1. 恢复旧版本的数据目录
bash
# 备份当前数据目录(可选,用于后续分析)
mv /var/lib/mysql /var/lib/mysql.current

# 恢复旧版本的数据目录
cp -r /var/lib/mysql.bak.8.0.28 /var/lib/mysql

# 恢复数据目录权限
chown -R mysql:mysql /var/lib/mysql
  1. 恢复旧版本的配置文件
bash
# 恢复配置文件
cp /etc/my.cnf.bak.8.0.28 /etc/my.cnf
  1. 启动 MySQL 服务
bash
systemctl start mysql
  1. 验证 MySQL 版本
bash
mysql -V
mysql Ver 8.0.28 for Linux on x86_64 (MySQL Community Server - GPL)
  1. 验证数据完整性
sql
-- 检查关键表的数据行数
SELECT COUNT(*) FROM db1.table1;
SELECT COUNT(*) FROM db1.table2;

-- 验证存储过程和触发器
CALL db1.sp_test();
  1. 恢复写入流量
sql
SET GLOBAL read_only = OFF;

Logical 升级的回滚步骤

Logical 升级的回滚主要是将应用连接切换回旧版本的数据库实例。

回滚步骤

  1. 确认旧版本数据库实例状态

确保旧版本数据库实例处于正常运行状态:

bash
systemctl status mysql_old
  1. 验证旧版本数据完整性
sql
-- 检查关键表的数据行数
SELECT COUNT(*) FROM db1.table1;
SELECT COUNT(*) FROM db1.table2;

-- 验证存储过程和触发器
CALL db1.sp_test();
  1. 切换应用连接到旧版本数据库

更新应用配置,将数据库连接指向旧版本数据库实例:

  • 更新应用配置文件中的数据库连接信息
  • 重启应用程序

示例

bash
# 更新应用配置文件
sed -i 's/mysql_new/mysql_old/g' /etc/app/config.yml

# 重启应用
systemctl restart app
  1. 验证应用连接

验证应用程序能够正常连接到旧版本数据库并执行操作:

  • 执行简单的业务操作
  • 检查应用日志,确保没有连接错误
  1. 停止新版本数据库实例

如果不需要保留新版本数据库实例,可以将其停止:

bash
systemctl stop mysql_new

主从复制环境的回滚步骤

主从复制环境的回滚需要考虑主库和从库的回滚顺序和一致性。

回滚策略

主从复制环境的回滚主要有两种策略:

  • 整体回滚:将主库和所有从库都回滚到升级前的版本
  • 切换回滚:将业务流量切换到备用主库,然后回滚原主库

整体回滚步骤

  1. 停止所有从库的复制进程

在每个从库上执行:

sql
-- MySQL 5.6/5.7
STOP SLAVE;

-- MySQL 8.0
STOP REPLICA;
  1. 停止所有从库的 MySQL 服务
bash
systemctl stop mysql_slave1
systemctl stop mysql_slave2
# ... 停止其他从库
  1. 回滚所有从库(参考 In-place 升级或 Logical 升级的回滚步骤)

  2. 停止主库的 MySQL 服务

bash
systemctl stop mysql_master
  1. 回滚主库(参考 In-place 升级或 Logical 升级的回滚步骤)

  2. 启动主库的 MySQL 服务

bash
systemctl start mysql_master
  1. 重新配置主从复制
  • 在主库上创建复制用户(如果需要)
  • 在每个从库上重新配置复制关系

示例

sql
-- 在主库上创建复制用户
CREATE USER 'repl'@'%' IDENTIFIED BY 'repl_password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';

-- 查看主库状态
SHOW MASTER STATUS;

-- 在从库上配置复制
-- MySQL 5.6/5.7
CHANGE MASTER TO
  MASTER_HOST='master_host',
  MASTER_USER='repl',
  MASTER_PASSWORD='repl_password',
  MASTER_LOG_FILE='binlog.000001',
  MASTER_LOG_POS=154;

START SLAVE;

-- MySQL 8.0
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='master_host',
  SOURCE_USER='repl',
  SOURCE_PASSWORD='repl_password',
  SOURCE_LOG_FILE='binlog.000001',
  SOURCE_LOG_POS=154;

START REPLICA;
  1. 启动所有从库的 MySQL 服务
bash
systemctl start mysql_slave1
systemctl start mysql_slave2
# ... 启动其他从库
  1. 验证主从复制状态

在每个从库上执行:

sql
-- MySQL 5.6/5.7
SHOW SLAVE STATUS\G

-- MySQL 8.0
SHOW REPLICA STATUS\G

确保 Slave_IO_Running/Replica_IO_RunningSlave_SQL_Running/Replica_SQL_Running 都为 Yes,且 Seconds_Behind_Master/Seconds_Behind_Source 为 0。

  1. 恢复写入流量
sql
SET GLOBAL read_only = OFF;

切换回滚步骤

  1. 将业务流量切换到备用主库

更新应用配置,将数据库连接指向备用主库:

bash
# 更新应用配置文件
sed -i 's/master_host/standby_master_host/g' /etc/app/config.yml

# 重启应用
systemctl restart app
  1. 回滚原主库(参考 In-place 升级或 Logical 升级的回滚步骤)

  2. 将原主库配置为备用主库的从库

sql
-- MySQL 5.6/5.7
CHANGE MASTER TO
  MASTER_HOST='standby_master_host',
  MASTER_USER='repl',
  MASTER_PASSWORD='repl_password',
  MASTER_LOG_FILE='binlog.000001',
  MASTER_LOG_POS=154;

START SLAVE;

-- MySQL 8.0
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='standby_master_host',
  SOURCE_USER='repl',
  SOURCE_PASSWORD='repl_password',
  SOURCE_LOG_FILE='binlog.000001',
  SOURCE_LOG_POS=154;

START REPLICA;
  1. 验证复制状态
sql
-- MySQL 5.6/5.7
SHOW SLAVE STATUS\G

-- MySQL 8.0
SHOW REPLICA STATUS\G

回滚后的验证

回滚完成后,需要进行以下验证工作:

服务状态验证

  • 验证 MySQL 服务是否正常运行
  • 验证监听端口是否正常
  • 验证进程是否正常

示例

bash
# 验证 MySQL 服务状态
systemctl status mysql

# 验证监听端口
netstat -tlnp | grep 3306

# 验证进程
ps aux | grep mysqld

数据完整性验证

  • 验证关键表的数据行数是否与升级前一致
  • 验证数据内容是否正确
  • 验证存储过程、触发器、视图等对象是否正常

示例

sql
-- 检查关键表的数据行数
SELECT COUNT(*) FROM db1.table1;
SELECT COUNT(*) FROM db1.table2;

-- 验证数据内容
SELECT * FROM db1.table1 WHERE id = 123;

-- 验证存储过程和触发器
CALL db1.sp_test();

应用功能验证

  • 验证应用程序能够正常连接到数据库
  • 验证关键业务功能是否正常
  • 验证应用性能是否正常

示例

bash
# 执行应用健康检查
curl -i http://app_host/health

# 执行业务功能测试
curl -i -X POST http://app_host/api/test -d '{"data": "test"}'

性能验证

  • 验证系统性能是否恢复到升级前水平
  • 验证查询响应时间是否正常
  • 验证系统资源使用率是否正常

示例

bash
# 使用 sysbench 进行性能测试
sysbench --db-driver=mysql --mysql-host=localhost --mysql-user=root --mysql-password=your_password --mysql-db=test --table_size=1000000 --tables=10 --threads=64 oltp_read_write run

# 查看系统资源使用率
top
vmstat 1

日志验证

  • 检查 MySQL 错误日志,确保没有错误
  • 检查慢查询日志,确保没有异常慢查询
  • 检查系统日志,确保没有相关错误

示例

bash
# 检查 MySQL 错误日志
tail -n 100 /var/log/mysql/error.log

# 检查慢查询日志
tail -n 100 /var/log/mysql/mysql-slow.log

# 检查系统日志
tail -n 100 /var/log/messages

版本差异考虑

MySQL 5.6 回滚注意事项

  • 配置文件差异:MySQL 5.6 配置文件中某些参数在高版本中可能被废弃或重命名
  • 系统表结构:MySQL 5.6 系统表结构与 5.7/8.0 差异较大,回滚时需确保系统表完整
  • 复制命令:MySQL 5.6 仅支持旧版复制命令,如 STOP SLAVESTART SLAVE
  • 存储引擎:MySQL 5.6 中 MyISAM 存储引擎仍被广泛使用,回滚时需注意
  • 性能 Schema:MySQL 5.6 中 Performance Schema 默认关闭,回滚后需根据需要开启

MySQL 5.7 回滚注意事项

  • SQL_MODE 默认值:MySQL 5.7 默认 SQL_MODE 更加严格,回滚后需注意应用兼容性
  • 密码策略:MySQL 5.7 默认启用 validate_password 插件,回滚后需注意密码策略
  • InnoDB 变化:MySQL 5.7 中 InnoDB 成为默认存储引擎,回滚后需确保 InnoDB 相关配置正确
  • Performance Schema 增强:MySQL 5.7 中 Performance Schema 收集更多性能数据,回滚后需注意资源占用

MySQL 8.0 回滚注意事项

  • 认证插件变化:MySQL 8.0 默认认证插件为 caching_sha2_password,回滚后需注意应用驱动兼容性
  • 数据字典变化:MySQL 8.0 引入了新的数据字典,回滚时需确保数据字典完整
  • 复制命令变化:MySQL 8.0 引入了新的复制命令,如 STOP REPLICASTART REPLICA
  • 系统变量变化:MySQL 8.0 中大量系统变量被重命名或废弃,回滚时需注意配置文件兼容性
  • 错误日志格式:MySQL 8.0 错误日志格式更加结构化,回滚后需注意日志监控工具兼容性

回滚过程中的监控和问题处理

实时监控

在回滚过程中,需要实时监控以下指标:

  • 系统资源:CPU、内存、磁盘 I/O、网络流量等
  • MySQL 状态:连接数、QPS、TPS、锁等待等
  • 复制状态:主从延迟、复制错误等(如果是主从复制环境)
  • 日志信息:错误日志、慢查询日志等

常见问题和解决方案

MySQL 服务无法启动

问题现象

systemctl start mysql
Job for mysql.service failed because the control process exited with error code.

解决方案

  • 查看错误日志,定位具体问题
  • 检查数据目录权限是否正确
  • 检查配置文件是否有语法错误
  • 检查端口是否被占用
  • 尝试使用 --skip-grant-tables 参数启动,修复权限问题

示例

bash
# 查看错误日志
tail -n 100 /var/log/mysql/error.log

# 检查数据目录权限
chown -R mysql:mysql /var/lib/mysql

# 检查配置文件语法
mysqld --defaults-file=/etc/my.cnf --validate-config

# 使用 --skip-grant-tables 参数启动
mysqld_safe --skip-grant-tables --defaults-file=/etc/my.cnf &

数据一致性问题

问题现象

  • 回滚后数据与升级前不一致
  • 主从复制出现数据不一致

解决方案

  • 使用备份恢复数据
  • 使用 pt-table-checksumpt-table-sync 工具修复主从数据不一致
  • 重新初始化从库

示例

bash
# 使用 xtrabackup 恢复数据
xtrabackup --copy-back --target-dir=/backup/mysql_full_20231201
chown -R mysql:mysql /var/lib/mysql

# 使用 pt-table-checksum 检查主从数据一致性
pt-table-checksum --host=master_host --user=root --password=your_password

# 使用 pt-table-sync 修复主从数据不一致
pt-table-sync --execute --replicate=percona.checksums master_host

应用连接失败

问题现象

ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (111)

解决方案

  • 检查 MySQL 服务是否正常运行
  • 检查防火墙是否开放了 3306 端口
  • 检查用户权限是否允许远程连接
  • 检查密码是否正确
  • 检查应用配置中的连接信息是否正确

示例

bash
# 检查 MySQL 服务状态
systemctl status mysql

# 检查防火墙规则
iptables -L -n | grep 3306

# 检查用户权限
mysql -u root -p -e "SELECT User, Host FROM mysql.user WHERE User='app_user'"

# 检查应用配置
cat /etc/app/config.yml | grep -A 10 database

最佳实践

回滚方案设计最佳实践

  • 提前测试:在测试环境中测试回滚方案,确保其可行性
  • 详细文档:回滚方案必须详细记录在文档中,包括步骤、命令、注意事项等
  • 明确责任:明确回滚过程中各个角色的责任和分工
  • 时间估算:估算回滚所需的时间,以便更好地规划和沟通
  • 备份策略:确保有完整的备份,包括数据、配置和日志
  • 监控机制:建立回滚过程中的监控机制,及时发现问题

回滚执行最佳实践

  • 快速执行:回滚操作必须快速执行,减少业务 downtime
  • 严格按照流程执行:严格按照回滚方案的步骤执行,避免出错
  • 记录过程:记录回滚过程中的所有操作和事件,便于后续分析
  • 及时沟通:及时向相关人员通报回滚进展和结果
  • 谨慎操作:回滚过程中要谨慎操作,避免二次错误

回滚后的最佳实践

  • 总结分析:对回滚原因、过程和结果进行总结分析,提出改进措施
  • 更新文档:根据回滚经验,更新升级和回滚文档
  • 培训学习:组织团队学习回滚经验,提高团队的故障处理能力
  • 改进流程:根据回滚经验,改进升级流程和回滚方案

案例分析

案例一:In-place 升级失败回滚

案例背景

  • 当前版本:MySQL 5.7.35
  • 目标版本:MySQL 8.0.33
  • 升级方式:In-place 升级
  • 升级过程中出现 MySQL 服务无法启动的问题

回滚过程

  1. 问题发现:升级后执行 systemctl start mysql 失败,查看错误日志发现系统表损坏
  2. 决策制定:由于问题严重且无法在短时间内解决,决定执行回滚
  3. 回滚执行
    • 停止 MySQL 服务
    • 恢复 5.7.35 版本的二进制文件
    • 恢复数据目录和配置文件
    • 启动 MySQL 服务
  4. 回滚验证
    • 验证 MySQL 版本为 5.7.35
    • 验证数据完整性
    • 验证应用连接正常
  5. 总结分析:升级失败原因是系统表兼容性问题,改进措施是在升级前使用 mysqlcheck --check-upgrade 工具检查系统表

回滚效果

  • 回滚时间:30 分钟
  • 业务 downtime:45 分钟
  • 数据无丢失

案例二:Logical 升级后性能下降回滚

案例背景

  • 当前版本:MySQL 8.0.28
  • 目标版本:MySQL 8.0.33
  • 升级方式:Logical 升级
  • 升级后应用性能严重下降

回滚过程

  1. 问题发现:升级后应用响应时间从 50ms 增加到 500ms,严重影响用户体验
  2. 决策制定:经过性能分析,确定是新版本查询优化器行为变化导致,短期内无法优化,决定执行回滚
  3. 回滚执行
    • 验证旧版本数据库状态正常
    • 切换应用连接回旧版本数据库
    • 验证应用性能恢复正常
  4. 回滚验证
    • 验证应用响应时间恢复到 50ms 左右
    • 验证业务功能正常
  5. 总结分析:升级失败原因是新版本查询优化器对某些查询的处理方式发生变化,改进措施是在升级前对所有慢查询进行测试

回滚效果

  • 回滚时间:15 分钟
  • 业务 downtime:20 分钟
  • 数据无丢失

总结

回滚方案是 MySQL 升级过程中的重要组成部分,它可以在升级失败或出现严重问题时,快速将系统恢复到升级前的状态,最大限度地减少业务损失。

一个完善的回滚方案应该包括:

  • 明确的回滚触发条件
  • 详细的回滚前准备工作
  • 清晰的回滚步骤
  • 回滚后的验证方法
  • 回滚过程中的监控和问题处理机制
  • 版本差异考虑
  • 最佳实践和案例分析

回滚方案需要提前测试和文档化,确保在实际情况下能够快速、可靠地执行。同时,回滚后的总结分析也是非常重要的,它可以帮助我们改进升级流程和回滚方案,提高未来升级的成功率。

作为 DBA,我们应该重视回滚方案的设计和测试,将其作为升级工作的重要组成部分,确保数据库升级的安全和可靠。