MySQL 5.7 作为备受信赖的服务版本,拥有广泛的用户群体。社区 MySQL 5.7 已于 2023 年 10 月起停止维护,不再进行缺陷的修复和功能的更新。为了满足用户的使用需求,阿里云 RDS 提供了便捷的解决方案,可以轻松地将 RDS MySQL 5.7 版本升级到 8.0 版本。
背景信息
2015 年 10 月,Oracle 正式推出 MySQL 5.7。相对于 MySQL 5.6,MySQL 5.7 具备更出色的性能、更丰富的功能以及更高的安全性,逐渐成为了后续 MySQL 版本的首选。2018 年,MySQL 8.0 发布,相比 MySQL 5.7 有了进一步的提升。2023 年 10 月 25 日,社区推出 MySQL 5.7.44,该版本为最后一个 5.7 版本,意味着社区将停止对 MySQL 5.7 的维护,不再进行缺陷的修复和功能的更新。同时,社区也鼓励用户将 MySQL 5.7 升级到 MySQL 8.0。
MySQL 5.7 作为备受信赖的服务版本,目前仍然拥有广泛的用户群体。截止至 2024 年 1 月,在阿里云 RDS MySQL 的用户群中,约有 46%的用户仍然坚持使用 5.7 作为他们的主要服务版本。然而,随着生命周期的结束,由于缺乏后续的更新和支持,MySQL 5.7 面临着各种问题,难以满足不断变化的需求。社区 MySQL 主要服务版本如下图所示。
相对于 MySQL 5.7 版本,MySQL 8.0 版本引入了许多强大而实用的特性。
-
Atomic DDL :解决了 MySQL 5.7 版本中 DDL 中断导致文件残留的问题,确保了文件操作的原子性,同时解决了 Server 层和引擎层数据不一致的问题。
-
Instant DDL :大大缩短 DDL 执行的时长,快速完成表结构的变更,同时消除了 Binlog 复制延迟。
-
增强 JSON 支持 :新增了多个与 JSON 相关的函数,并对 JSON 的内存使用进行了优化。
-
隐藏索引 :可以将索引设置对优化器不可见,从而避免潜在的重复删除和重建索引的操作。
-
克隆插件 :相比传统的复制方式更加高效,直接在源实例上进行授权和克隆,极大地提高了 MySQL 的可扩展性。
阿里云 RDS MySQL 8.0 在社区 MySQL 8.0 的基础上进行了深度优化,并开发了许多高级特性,包括:
-
Binlog in Redo :在事务提交时将 Binlog 内容同步写入到 Redo Log 中,减少对磁盘的操作,提高数据库性能。
-
通用云盘 IO 加速 :通过扩展 InnoDB Buffer Pool,将一部分数据页和温数据缓存,使您在无任何成本变化和业务改动的情况下,就可以获得实例 IO 性能的大幅度提升。
-
冷热数据分离:通过将云盘归档数据转存至 OSS 端,大幅降低存储成本。
-
RTO 相关优化:通过优化 Redo 应用过程和启动路径等,大幅缩短实例启动时间,提升实例的可用性。
通过架构的持续演进和内核的不断优化,阿里云 RDS MySQL 相较社区版本拥有更高的稳定性和安全性、更强的性能和更多的功能,为用户提供稳定可靠、简单易用的数据库服务。更多 RDS MySQL 自研的高级特性请参见 AliSQL 功能概览 。
作为更强大的分支,MySQL 8.0 的维护周期将会更长,并且将逐步解决现有问题。长期来看,强烈建议个人用户和企业用户将 MySQL 5.7 升级至 MySQL 8.0。
社区版 MySQL 5.7 升级 8.0 遇到的问题
升级之前需要大量的准备工作
尽管存在许多必须升级的理由,但实际上从 MySQL 5.7 升级到 8.0 并非易事。MySQL 8.0 相较于 MySQL 5.7,在数据字典、存储引擎和 SQL 语法等方面发生了许多重大变化。由于不同版本之间存在差异,在进行升级之前需要充分评估并做好充足的准备工作。MySQL 8.0 相较于 MySQL 5.7 的主要差异如下图所示,详情请参见 MySQL 社区文档 。
MySQL 5.7 升级到 MySQL 8.0 是通过 inplace upgrade 的方式进行的,即直接使用 MySQL 8.0 的二进制文件来启动 MySQL 5.7 的实例,升级程序将自动进行相关的检查和升级。
升级前的准备工作实际上是为了消除升级程序中无法自动处理的“不兼容的”变化。主要工作包括:
-
MySQL 8.0 引入了全新的数据字典用于保存数据库中的元信息,Server 层和 InnoDB 层共享一份元数据。MySQL 8.0 的 information_schema 中的视图全部源自于数据字典表,与 InnoDB 相关的视图被重命名(INNODB_SYS_XXX 重命名为 INNODB_XXX)。若用户的应用依赖于这些视图,需要确保应用已做出相应的修改。在升级前需要确认业务是否依赖于 MySQL 8.0 中删除的系统表(如 mysql.proc、mysql.event 等),若存在则需使用新的访问方式去获取所需的数据。此外,对于和 MySQL 8.0 系统表同名的用户表(如 catalogs、routines 等),需要手动执行
RENAME/DROP TABLE
操作。 -
RDS MySQL 8.0 不再支持部分旧版本数据类型。如果表中字段含有 MySQL 8.0 不支持的数据类型,需要在升级前通过
REPAIR TABLE
或逻辑导出+导入的方式修复。更多信息请参见 准备升级安装 。 -
MySQL 8.0 中,分区表的处理已由 Server 层下沉至引擎层,MySQL Server 不再支持通用分区。Non-native 的分区表在 MySQL 8.0 中被废弃,因此需要将这类分区表改为 Native 类型(如 InnoDB 引擎)或是删除该分区表。
-
较早版本的 MySQL 5.7(5.0.17 之前的版本)的触发器(Triggers)不支持 DEFINER 属性,因此在 MySQL 5.7 实例中可能会存在缺失 DEFINER 属性的触发器,这些触发器会导致升级 MySQL 8.0 失败。在升级前您需要重新创建这些缺失 DEFINER 属性的触发器。
-
MySQL 8.0 中,外键约束的长度存在限制,如果外键约束过长,在升级过程中可能会导致错误和失败。因此,在进行升级之前,需要将问题外键修改为长度小于 64 个字符的约束。
-
MySQL 8.0 之前版本,视图的列名长度上限为 255 个字符,而在 MySQL 8.0 版本中,视图的列名长度上限缩短至 64 个字符。因此,在进行升级之前,需要将视图中过长的列名修改为长度小于 64 个字符的列名。
-
MySQL 8.0 中,单个 ENUM 和 SET 列元素的长度不得超过 255 个字符或 1020 个字节,因此在升级之前需要修改超出限制的 ENUM 和 SET。
-
如果.frm 文件和 InnoDB 数据字典表元数据信息不一致,那么会导致升级错误。因此,在进行升级之前,需要对数据进行逻辑导出和导入。若存在游离的.frm 文件(即不含.ibd 文件仅有.frm 文件),则需要进行相应清理。
-
MySQL 8.0 废弃了部分空间函数。如果生成列中包含了已删除的函数(新引入的空间函数以"ST"和"MBR"开头),则需要在升级前对相应的列进行修改。
-
在进行升级前,必须确保 MySQL 5.7 的实例已经进行了正确的关闭,即需要确保在升级前不存在待应用的 REDO 日志和等待回滚的事务。
-
MySQL 8.0 中引入了一些新的保留字,其中大多数禁止用作表名、列名等,因此需要对 MySQL 5.7 中包含的 MySQL 8.0 引入的新保留字做处理。
-
在升级前,需要将废弃的 sql_mode 变量内容(例如 NO_AUTO_CREATE_USER 等)修改为 MySQL 8.0 支持的模式,以避免导致实例无法启动。
-
较新版本的 MySQL 8.0 实例(8.0.13 及之后的版本),共享表空间(系统表空间和通用表空间)中不允许存在 InnoDB 类型的分区表,因此在进行升级前需要将共享表空间移动至独立表空间。
-
MySQL 8.0 实例中,GROUP BY 子句不支持 ASC 或 DESC 排序规则,因此,在进行升级前,需要修改或删除包含相应句法的存储过程。
-
在 MySQL 5.7 中,lower_case_table_names 是一个可修改的值,但在 MySQL 8.0 中,lower_case_table_names 是一个初始化参数,一旦实例初始化完成,就无法在后续进行修改。在进行升级时,若需要将该参数值修改为 1,请确保升级前库表名称为小写,以避免出现升级错误。
-
MySQL 5.7 和 MySQL 8.0 的字符集(character set)和排序规则(collation)的配置可能存在差异,这可能导致索引失效、查询报错等问题,例如:
-
MySQL 8.0 默认字符集为 utf8mb4,使用 1~4 字节存储一个字符,MySQL 5.7 默认字符集为 utf8mb3,使用 1~3 字节存储一个字符,因此字段和索引长度会受到影响。在 REDUNDANT 或者 COMPACT 行格式下,InnoDB 引擎所允许的最大索引长度为 767 字节,对应在 5.7 中索引允许的最大字符数是 255,而在 8.0 中则无法创建超过 191 字符的索引。
-
当在 MySQL 5.7 中使用了 utf8mb3 字符集创建表,如果升级 MySQL 8.0 后默认使用的字符集为 utf8mb4,新建表在没有指定 character set 时默认使用 utf8mb4。当新表(utf8mb4)和迁移表(utf8mb3)相关字段发生 JOIN 时,会因为 JOIN 两端字符集类型不一致导致索引失效。
-
当在 MySQL 5.7 中使用了 utf8mb4_general_ci 排序规则创建表,如果升级到 MySQL 8.0 后,默认使用的排序规则为 utf8mb4_0900_ai_ci,新建表在没有指定 collation 时默认使用 utf8mb4_0900_ai_ci。由于 utf8mb4_general_ci 和 utf8mb4_0900_ai_ci 优先级相同(无法选择排序规则),当新表和迁移表相关字段发生 JOIN 时,会因为 JOIN 两端排序规则不兼容导致报错。
为避免出现字符集引发的问题,在升级前需要检查 MySQL 中的字符集和排序规则使用情况,您也可以通过下述 SQL 来修改库、表、字段的字符集和排序规则。
# 修改库的字符集和排序规则 ALTER DATABASE database_name CHARACTER SET = charset COLLATE = collation; # 修改表的字符集和排序规则 ALTER TABLE table_name CONVERT TO CHARACTER SET charset COLLATE collation; # 修改字段的字符集和排序规则 ALTER TABLE table_name CHANGE column_name column_name type CHARACTER SET charset COLLATE collation;
重要-
修改列的 charset 时,MySQL 会尝试映射数据值,但如果修改前后 charset 不兼容,可能会发生数据丢失。
-
在阿里云 RDS MySQL 中,各版本默认字符集均使用 utf8mb3,默认排序规则均使用 utf8mb3_general_ci,如果没有手动修改过字符集和排序规则,大版本升级前后不会出现此类问题。
-
更多信息可参考: 调整实例 character_set_server 参数和 collation_server 参数 和 MySQL 社区字符集配置 。
-
除了上述的准备工作外,版本间存在的驱动、错误码、参数、优化器差异等也需要业务上做适配。对于自建用户,在进行 MySQL 5.7 到 MySQL 8.0 的大版本升级前,可以利用 MySQL Shell、mysqlcheck 工具和社区的升级检查文档检查准备工作。MySQL 5.7 升级到 MySQL 8.0 变化和检查细节请参见 MySQL 社区文档 。
经常升级失败并且难以分析失败原因
尽管社区提供了相应的检查工具和帮助文档,以帮助用户进行升级前的检查和问题解决。然而在实际操作中经常会遇到升级失败的情况,出现各种各样的错误,并且很难从日志信息中逐一分析升级失败的原因。升级失败分析困难的原因包括但不限于以下几点:
-
报错不明确。不同原因导致的错误,在日志中可能会记录为相同或相似的错误。用户需要投入大量时间和精力来排查错误的根本原因,这增加了升级的难度。
-
报错不完整。在问题出现后,针对性地解决后发现重复的问题再次导致失败。这是因为升级程序在首次检测到某一类错误时就提前退出,导致相同问题无法一次性全部被检测到。用户需要进行多次尝试和排查,这增加了升级的复杂性和时间成本。
-
部分错误只提供了错误提示,却没有相应的解决方案。用户需要自行查找解决方案或向社区寻求帮助,这增加了升级过程中的困惑和不确定性。
-
部分 MySQL 5.7 或更早版本存在的问题已经得到修复,但对存量实例的处理仍然存在不足。
在解决大版本升级问题中发现,由于版本差异以及更老的 MySQL 5.x 版本信息残留,大版本升级涉及的问题琐碎繁多,升级失败的根源大多集中在数据字典上。
使用 RDS MySQL 轻松升级到 RDS MySQL 8.0
阿里云 RDS MySQL 拥有大量正在使用的实例,每个月都有一些实例从 5.7 版本升级到 8.0 版本。在这一过程中,阿里云 RDS 团队积累了丰富的经验,建立了完善的预检查逻辑,并对多个大版本升级相关的 Bug 进行了分析和修复。
阿里云 RDS MySQL 团队致力于提前发现升级问题,保障升级平稳进行,提升大版本升级成功率,使用户获得更顺畅的升级体验。升级后用户可以享受到阿里云 RDS MySQL 8.0 带来的高性能和可靠性。此外,RDS 将延长对不同版本的 MySQL 的支持期限,为用户提供更充裕的缓冲时间。社区 MySQL 和阿里云 RDS MySQL 生命周期如下:
数据库大版本 |
社区发布时间 |
RDS 发布时间 |
社区生命周期结束时间 |
RDS 停止支持时间 |
MySQL 8.0 |
2018 年 04 月 19 日 |
2019 年 05 月 |
预计 2026 年 04 月 |
预计 2027 年 04 月 19 日 |
MySQL 5.7 |
2015 年 10 月 21 日 |
2016 年 11 月 |
2023 年 10 月 25 日 |
预计 2024 年 10 月 21 日 |
在自建 MySQL 5.7 升级至 8.0 的过程中,除了前期繁琐的检查工作外,根据报错信息逐步进行修复可能需要多次进行,并且可能会遇到难以修复的问题,导致无法完成升级的情况。升级过程中停机时间长,返工次数多,耗费时间和精力。
与自行升级用户相比,用户进行阿里云 RDS MySQL 的大版本升级时,无需进行繁琐的检查,也无需反复核查错误日志并逐一解决问题,仅需点击控制台上的升级按钮,所有升级动作将在后台自动完成。
-
针对线上常见的大版本升级失败问题,阿里云 RDS MySQL 内核已进行修复工作。当用户的实例存在修复覆盖下的问题时,升级过程将不会失败,从而可实现无感升级。
-
针对内核侧无法直接修复的问题,阿里云 RDS MySQL 管控侧主动进行了多项兼容性检测。在实例触发大版本升级动作前,提前发现问题并进行提示,用户在无需重复启停的情况下就能够解决大部分问题。同时,对于升级失败的节点会自动回滚到升级前的状态,不会影响业务运行。
自建 MySQL 5.7 升级到 8.0 和阿里云 RDS MySQL 5.7 升级到 8.0 流程对比如下:
经过一系列的治理,2023 年至 2024 年期间,阿里云 RDS MySQL 5.7 成功升级至 8.0 的比例高达 97.3%以上。阿里云 RDS MySQL 5.7 升级 8.0 的部分治理工作如下表所示:
导致升级失败的问题 |
阿里云 RDS 的治理 |
是否需要用户进一步干预 |
Server 层与 InnoDB 字段、索引大小写不一致 |
在升级过程中,系统会主动检测并修复 Server 层和 InnoDB 之间的不一致问题,用户无需手动干预即可实现升级。 |
无需用户操作。 |
表、字段和索引包含乱码 |
在升级过程中,系统将主动检测并清除表、字段和索引中无效的乱码信息,用户无需手动检查和处理。 |
无需用户操作。 |
存储过程和函数包含乱码 |
在预检查逻辑中,定位包含乱码信息的存储过程和函数,并向用户提示错误信息。 |
需要用户处理存储过程和函数中的乱码信息。 |
5.x 老版本临时表残留 |
在升级过程中,主动清除系统表中临时表残留的元信息,解决用户无法完成升级的问题。 |
无需用户操作。 |
外键分区表不兼容 |
在预检查逻辑中,定位包含外键和分区表冲突的表信息,方便用户提前处理。 |
需要用户解决表中外键和分区表的冲突。 |
空间列和 B-tree 索引不兼容 |
在预检查逻辑中,定位包含空间列和 B-tree 索引冲突的表信息,方便用户提前处理。 |
需要用户解决表中空间列和 B-tree 索引的冲突。 |
外键约束、视图列名、ENUM/SET 长度超限制 |
在预检查逻辑中,定位不满足长度要求的外键约束、视图列名和 ENUM/SET 字段,方便用户提前处理。 |
需要用户修改超出限制的长度。 |
包含 discard 的表空间 |
在预检查逻辑中,定位 discard 的表空间,方便用户提前处理。 |
需要用户导入表空间或删除表信息。 |
包含待 REPAIR 的表 |
在预检查逻辑中,定位待 REPAIR 的表,方便用户提前处理。 |
需要用户执行 REPAIR 的操作。 |
升级方法
关于 RDS MySQL 的升级,详细信息请参见 升级数据库版本 。
升级案例
案例一:Server 层与 InnoDB 字段、索引大小写不一致导致升级失败
-
自建 MySQL 升级失败:
[ERROR] [MY-012071] [InnoDB] Column name mismatch: From InnoDB: id_c From Server: Id_c [ERROR] [MY-010767] [Server] Error in fixing SE data for test.t3 [ERROR] [MY-010022] [Server] Failed to Populate DD tables. [ERROR] [MY-010119] [Server] Aborting
-
RDS MySQL 自动修正引擎元数据,升级成功:
[Warning] [MY-012071] [InnoDB] Column name case mismatch: From InnoDB: id_c From Server: Id_c, DD will be consistent with Server one. [Note] [MY-011088] [Server] Data dictionary initializing version '80023'.
案例二:表、字段、索引和存储过程包含乱码导致升级失败
-
自建 MySQL 升级失败:
[ERROR] [MY-013140] [Server] Comment for table 'test.t4' contains an invalid utf8mb3 character string: '\xF0\x9F\x90'. [ERROR] [MY-010022] [Server] Failed to Populate DD tables. [ERROR] [MY-010119] [Server] Aborting
-
RDS MySQL 自动清除表、字段和索引注释的乱码,升级成功:
[Warning] [MY-025076] [Server] Clear utf8mb3 character strings: '\xF0\x9F\x90' in comment of table 'test.t4'. [Note] [MY-011088] [Server] Data dictionary initializing version '80023'.
案例三:5.x 老版本临时表残留导致升级失败
-
自建 MySQL 升级失败:
[ERROR] [MY-012216] [InnoDB] Cannot open datafile for read-only: '/tmp/#sql1dc6f_2_0.ibd' OS error: 71 [ERROR] [MY-010020] [Server] Data Dictionary initialization failed. [ERROR] [MY-010119] [Server] Aborting
-
RDS MySQL 自动清理残留的元信息,升级成功:
[ERROR] [MY-012216] [InnoDB] Cannot open datafile for read-only: '/tmp/#sql8947_1_0.ibd' OS error: 71 [Warning] [MY-025078] [InnoDB] Skip orphaned tablespace during upgrade, space_id: 7, filepath: /tmp/#sql8947_1_0.ibd. [Note] [MY-011088] [Server] Data dictionary initializing version '80023'.
当然,阿里云 RDS MySQL 团队并未完全解决 MySQL 5.7 升级到 8.0 过程中的所有问题,在场景和方案上仍然存在需要完善的地方。
-
当前,在大版本升级过程中,为了确保用户实例不受影响,升级过程首先会在源实例的克隆实例上进行。当克隆实例完成升级后,再将用户实例进行切换,并释放旧版本实例。这个过程难免会消耗更多的时间和资源,因此有必要探索更好、更快的解决方案。
-
目前的治理手段无法覆盖所有的升级失败场景,无法保证 100%的升级成功率。同时,用户行为导致升级失败的情况,通常需要用户介入进一步处理,无法实现完全的全自动升级。因此,进一步降低用户干预的比例是需要持续努力的方向。
总结
在进行大版本升级时,必须进行详细的升级前检查,逐步解决升级过程中出现的问题,并且完成升级后的验证和测试工作。大版本升级失败问题的解决并非一蹴而就。阿里云 RDS MySQL 团队一直在进行相关问题的修复和建设,提供了简便和高效的大版本升级机制。随着 8.0 版本的稳定推进,大版本升级的问题会逐步得到解决,直至完全消除。