Skip to content

Azure MySQL 5.7 升级到 8.0 遇到的几个问题、对策及复盘

🏷️ Azure MySQL

由于 MySQL 5.7 的支持将于今年(2023)10 月结束,部分项目要求升级到 8.0。产线环境 MySQL 使用的是 Azure 上的单一服务器,本次需要升级到 8.0 版本的灵活服务器。

Azure 部分区已经提供了通过门户网站升级的方式,不过可惜本次需要升级的 MySQL 还没有开放这个功能。

手动升级的详细步骤可以参考官方文档。其中与本次升级一个比较大的不同是:官方文档中说在备份数据时 main”线程只需将全局锁保留到“dump”线程可以启动事务为止 ,但是 Azure 工程师给的方案中建议在备份时数据库应设置为只读,且全局锁也需要一直保留到备份结束。

本次升级的脚本是由 Azure 的工程师提供的,由于有敏感信息,这里不方便贴,不过整体的流程和官方文档基本一致,只是整理成了脚本,简化了升级的操作。

这里记录下产线环境升级过程中遇到的几个问题及解决方案。

问题及对策

  1. 备份数据库时报错:Error connection to database: SSL connection is required. Please specify SSL options and retry.

    这个是由于 MySQL 开启了强制 SSL 连接,而备份的客户端并没有正确的配置 SSL 证书导致的。

    对策:简单点的做法就是把 强制 SSL 连接 暂时关闭掉,等备份完了再开。

  2. 还原数据时报错: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,然后还要导入这张表对应的数据。

    导入时可以使用类似如下命令:

    bash
    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. 还原数据时报错: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 里是导出的存储过程、函数、事件等的定义。而这个报错是由于方法中使用到的用户没有创建导致的。

    究其原因还是这边操作的疏忽,升级准备阶段中有一步操作就是创建一样的用户,但是升级时只创建了以为需要的用户。

    对策:将缺失的用户创建后再次导入这些函数就可以了。

    bash
    mysql -u user_name -p database_name < database_name-schema-post.sql
  4. 还原数据时报错:Error restoring 1: Failed to generate invisible primary key. Auto-increment column already exists.

    直接原因是 Table 中有一个 id 自增列,但是还有一个只有 id 的唯一索引。

    sql
    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)即可。

    sql
    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';

    之后再次导入表结构和数据:

    bash
    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

复盘

事后复盘,个人感觉遇到的几个问题其实都是有可能避免的。总结下来有如下几点:

  1. 应该在每个升级前在测试环境做一次模拟升级。

    这个是做的比较好的点。这样能发现大部分的问题,包括流程上和业务上的。而且模拟操作过一次后,在产线环境操作时风险会降低很多。另外生产环境升级时还执行了详细的计划书。

    本次共升级了 3 个 MySQL 服务器,除了上面几个小问题外,没有发生大的问题,没有触发回滚,也没有造成业务上的影响。

  2. 应保持测试环境和产线环境配置和结构的统一。

    虽然都做了模拟升级,但是由于具体的环境上还是有些不一样的地方,导致在测试环境执行好好的,到了生产环境就报错了。

    上面列出来的几个问题都有这方面的原因。比如强制 SSL 连接,测试环境一般都是不打开的。建表失败的几个也是如此,这几张表基本上都是废弃的表,生产环境估计是为了保留数据,这些不再使用的表也没有被删除掉。而恰恰是这些废弃的表由于缺乏维护,出问题的几率反而会更高。

    平时项目维护时应尽量让测试环境和产线环境保持一致,这样一些问题在模拟升级时就可以提前发现了。

  3. 最好还是严格的按照升级步骤来执行,避免擅自省略或改变某些操作。

    比如上面的问题 3 就是这个原因导致。

  4. 建议删除废弃的或过期的数据(如日志之类的数据),以减少备份数据的时间。

    因为备份数据大都是需要停机的(MySQL 被设置为了只读,且添加了全局锁),所以减少备份的时间就等于减少了停机时间。本次升级中有一个库就删除了近一半的旧数据。

  5. 建议给恢复数据预留比较宽裕的时间。

    首先,恢复本身耗费的时间就比较久,而且从这几次升级过程来看恢复时出问题的几率也比较大。

    其次,这个阶段预留的时间长一些对生产环境也不会有什么影响,最多也就影响后期同步数据的时间(其实一般并不会有太多的影响,同步的速度还是很快的)。

    本次升级的过程中一般至少预留了两天的时间。

最后还是要吐槽下 Azure 的产品升级速度,如果门户网站能提供自动升级的功能,也就不需要用户折腾这么复杂的升级流程了(关键是熬夜太累了啊~~~)。