相关文章推荐
活泼的瀑布  ·  不安装vc2015 ...·  2 年前    · 
爱热闹的充电器  ·  Windows Server 2012 ...·  2 年前    · 
潇洒的保温杯  ·  Material ...·  2 年前    · 
RDS MySQL助力MySQL 5.7升级8.0

RDS MySQL助力MySQL 5.7升级8.0

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。

image.png

MySQL 5.7 作为备受信赖的服务版本,目前仍然拥有广泛的用户群体。截止至 2024 1 月,在阿里云 RDS MySQL 的用户群中,约有 46%的用户仍然坚持使用 5.7 作为他们的主要服务版本。然而,随着生命周期的结束,由于缺乏后续的更新和支持,MySQL 5.7 面临着各种问题,难以满足不断变化的需求。社区 MySQL 主要服务版本如下图所示。

image.svg

相对于 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 社区文档

image

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;
    重要

除了上述的准备工作外,版本间存在的驱动、错误码、参数、优化器差异等也需要业务上做适配。对于自建用户,在进行 MySQL 5.7 MySQL 8.0 的大版本升级前,可以利用 MySQL Shell、mysqlcheck 工具和社区的升级检查文档检查准备工作。MySQL 5.7 升级到 MySQL 8.0 变化和检查细节请参见 MySQL 社区文档

经常升级失败并且难以分析失败原因

尽管社区提供了相应的检查工具和帮助文档,以帮助用户进行升级前的检查和问题解决。然而在实际操作中经常会遇到升级失败的情况,出现各种各样的错误,并且很难从日志信息中逐一分析升级失败的原因。升级失败分析困难的原因包括但不限于以下几点:

  1. 报错不明确。不同原因导致的错误,在日志中可能会记录为相同或相似的错误。用户需要投入大量时间和精力来排查错误的根本原因,这增加了升级的难度。

  2. 报错不完整。在问题出现后,针对性地解决后发现重复的问题再次导致失败。这是因为升级程序在首次检测到某一类错误时就提前退出,导致相同问题无法一次性全部被检测到。用户需要进行多次尝试和排查,这增加了升级的复杂性和时间成本。

  3. 部分错误只提供了错误提示,却没有相应的解决方案。用户需要自行查找解决方案或向社区寻求帮助,这增加了升级过程中的困惑和不确定性。

  4. 部分 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 流程对比如下:

image

经过一系列的治理,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 版本的稳定推进,大版本升级的问题会逐步得到解决,直至完全消除。

相关文档

  • 您可以将自建 MySQL 或第三方云数据库 MySQL 迁移至 RDS MySQL,利用 RDS 轻松升级到 MySQL 8.0。数据迁移请参见 数据迁移方案

  • 更多 MySQL 的信息,请参见 MySQL