由于 MySQL 5.7 的支持将于今年(2023)10月结束,部分项目要求升级到 8.0 。产线环境 MySQL 使用的是 Azure 上的单一服务器,本次需要升级到 8.0 版本的灵活服务器。
Azure 部分区已经提供了通过门户网站升级的方式,不过可惜本次需要升级的 MySQL 还没有开放这个功能。
手动升级的详细步骤可以参考官方文档。其中与本次升级一个比较大的不同是:官方文档中说在备份数据时 main”线程只需将全局锁保留到“dump”线程可以启动事务为止 ,但是 Azure 工程师给的方案中建议在备份时数据库应设置为只读,且全局锁也需要一直保留到备份结束。
本次升级的脚本是由 Azure 的工程师提供的,由于有敏感信息,这里不方便贴,不过整体的流程和官方文档基本一致,只是整理成了脚本,简化了升级的操作。
这里记录下产线环境升级过程中遇到的几个问题及解决方案。
问题及对策
备份数据库时报错:Error connection to database: SSL connection is required. Please specify SSL options and retry.
这个是由于 MySQL 开启了强制 SSL 连接,而备份的客户端并没有正确的配置 SSL 证书导致的。
对策:简单点的做法就是把 强制SSL连接 暂时关闭掉,等备份完了再开。
还原数据时报错:Error restoring 1: Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes is stored inline.
这个错误是由于数据行的字节长度超过 8126 从而导致建表失败。这张表里有好多个 varchar(255)
的列,但是实际数据只有一条(事后确认这是一张废弃的表)。
另外由于建表失败,这张表对应数据的还原也会失败,在还原结果页面可以看到除了 Schema 有错误之外,Data 也会有对应的报错。
对策:通过将表的 ROW_FORMAT
改为 DYNAMIC
来解决(已向微软确认,不影响主从同步)。
修改 mydumper 备份出来的 schema 文件,然后重新运行这个文件以创建 Table ,然后还要导入这张表对应的数据。
导入时可以使用类似如下命令:
mysql -u user_name -p database_name < database_name.table_name-schema.sql
mysql -u user_name -p database_name < database_name.table_name-schema.00000.sql
还原数据时报错:Error occurs between lines: 8 and 16 on file database_name-schema-post.sql: Access denied; you need (at least one of) the SUPER or SET_USER_ID privilege(s) for this operation
Error restoring 1: CREATE DEFINER=`db_user_name`@`%` FUNCTION `functionName`(st varchar(50)) RETURNS varchar(50) CHARSET utf8
schema-post.sql 里是导出的存储过程、函数、事件等的定义。而这个报错是由于方法中使用到的用户没有创建导致的。
究其原因还是这边操作的疏忽,升级准备阶段中有一步操作就是创建一样的用户,但是升级时只创建了以为需要的用户。
对策:将缺失的用户创建后再次导入这些函数就可以了。
mysql -u user_name -p database_name < database_name-schema-post.sql
还原数据时报错:Error restoring 1: Failed to generate invisible primary key. Auto-increment column already exists.
直接原因是 Table 中有一个 id
自增列,但是还有一个只有 id
的唯一索引。
CREATE TABLE `sample` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id',
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Sample';
比较奇怪的是,通过 desc table_name
查看该表时,可以看到 id
列是 PRI 的,但是通过 SHOW CREATE TABLE table_name
语句查看时只能看到唯一索引。
对策:将唯一索引(UNIQUE KEY
)修改为主键(PRIMARY KEY
)即可。
CREATE TABLE `sample` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Sample';
之后再次导入表结构和数据:
mysql -u user_name -p database_name < database_name.table_name-schema.sql
mysql -u user_name -p database_name < database_name.table_name-schema.00000.sql
复盘
事后复盘,个人感觉遇到的几个问题其实都是有可能避免的。总结下来有如下几点:
应该在每个升级前在测试环境做一次模拟升级。
这个是做的比较好的点。这样能发现大部分的问题,包括流程上和业务上的。而且模拟操作过一次后,在产线环境操作时风险会降低很多。另外生产环境升级时还执行了详细的计划书。
本次共升级了 3 个 MySQL 服务器,除了上面几个小问题外,没有发生大的问题,没有触发回滚,也没有造成业务上的影响。
应保持测试环境和产线环境配置和结构的统一。
虽然都做了模拟升级,但是由于具体的环境上还是有些不一样的地方,导致在测试环境执行好好的,到了生产环境就报错了。
上面列出来的几个问题都有这方面的原因。比如强制SSL连接,测试环境一般都是不打开的。建表失败的几个也是如此,这几张表基本上都是废弃的表,生产环境估计是为了保留数据,这些不再使用的表也没有被删除掉。而恰恰是这些废弃的表由于缺乏维护,出问题的几率反而会更高。
平时项目维护时应尽量让测试环境和产线环境保持一致,这样一些问题在模拟升级时就可以提前发现了。
最好还是严格的按照升级步骤来执行,避免擅自省略或改变某些操作。
比如上面的问题 3 就是这个原因导致。
建议删除废弃的或过期的数据(如日志之类的数据),以减少备份数据的时间。
因为备份数据大都是需要停机的(MySQL 被设置为了只读,且添加了全局锁),所以减少备份的时间就等于减少了停机时间。本次升级中有一个库就删除了近一半的旧数据。
建议给恢复数据预留比较宽裕的时间。
首先,恢复本身耗费的时间就比较久,而且从这几次升级过程来看恢复时出问题的几率也比较大。
其次,这个阶段预留的时间长一些对生产环境也不会有什么影响,最多也就影响后期同步数据的时间(其实一般并不会有太多的影响,同步的速度还是很快的)。
本次升级的过程中一般至少预留了两天的时间。
最后还是要吐槽下 Azure 的产品升级速度,如果门户网站能提供自动升级的功能,也就不需要用户折腾这么复杂的升级流程了(关键是熬夜太累了啊~~~)。