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;
    重要
    • 修改列的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社区文档

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

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

  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