亚马逊AWS官方博客

保驾护航 – Amazon RDS for MySQL 5.7 到 8.0 升级前置检查

1.前言

根据开源 MySQL 官方对 MySQL 5.7 的版本支持信息,首次发布于 2015 年 10 月 21 日的 MySQL 5.7 版本将在 2023 年 10 月 21 日迎来大版本的退役,也表示社区将终止或限制对 MySQL 5.7 版本的支持,以将重点转移到他们的新产品或版本上。鉴于社区版本 EOL 时间在即,没有特殊情况,我们也都会建议客户在处理小版本升级的时候可以实现跨 MySQL 5.7 升级到 Amazon RDS MySQL 8.0 以上版本。所以陆续有越来越多的客户面临着跨版本升级的需求,升级对于客户最关心的几个问题莫过于兼容性、升级影响时间、升级方案、新版本特性等问题。这篇博客主要就 Amazon RDS MySQL 5.7 到 Amazon RDS MySQL 8.0 升级前的兼容性项检查做一个详细的说明和展开。

2.兼容性检查工具

目前 MySQL 官方提供的 MySQL Shell 中有专门用作 MySQL 升级兼容性检查的函数 util.checkForServerUpgrade(),我们可以使用该检查函数在升级之前对相应实例进行初步检查,如果检查中出现升级不兼容项目,输出的日志中会有详细的信息记录,并且会在日志末尾对不兼容项目的数量按照 Error、Warnings 以及 Notices 进行分组统计其数量。对 Amazon RDS 进行实例升级时,升级检查结果记录在 Amazon RDS 的日志记录中,日志文件名为 PrePatchCompatibility.log,当出现直接导致升级失败的错误类型时,也会同时在实例对应 Event 中出现相应的日志记录。

Amazon RDS Event:

Amazon RDS Log:

兼容性检查函数 util.checkForServerUpgrade()会根据目标版本和源版本之间的更改定义兼容性检查项。下面是使用方法以及几种常见的工具使用错误场景。

1)MySQL Shell 升级检查函数 util.checkForServerUpgrade()具体的使用方法如下:

[root@ip-bin]#wget https://dev.mysql.com/get/Downloads/MySQL-Shell/mysql-shell-8.0.32-linux-glibc2.12-x86-64bit.tar.gz
[root@ip-bin]#tar -xvzf mysql-shell-8.0.32-linux-glibc2.12-x86-64bit.tar.gz
[root@ip-bin]#cd mysql-shell-8.0.32-linux-glibc2.12-x86-64bit/bin
[root@ip-bin]#./mysqlsh
MySQL JS > util.checkForServerUpgrade('admin@your-instance.rds.amazonaws.com:3306',{"password":"123456","targetVersion":"8.0.30"})
The MySQL server at your-instance.rds.amazonaws.com:3306,
version 5.7.38-log - Source distribution, will now be checked for compatibility
issues for upgrade to MySQL 8.0.30...


Errors: 0
Warnings: 4
Notices: 0

2)使用 MySQL Shell 工具进行前置检查的时候,需选择正确的工具版本。MySQL Shell 8.0.21 及更高版本可用于 RDS 检测,如果版本低于 8.0.21,会出现以下报错:

Util.checkForServerUpgrade: Access denied for user 'admin'@' (using password: YES) (MySQL Error 1045)

3)需要注意选择正确版本且保证待检测实例版本与工具版本相匹配。MySQL Shell 默认可检测 8.0.11 至工具版本一致的 MySQL Server 版本。如果选择了与工具版本不相符的实例进行检测,如工具版本为 8.0.21,用来检测 RDS MySQL 5.7.38 版本实例升级至 RDS MySQL 8.0.32 版本会出现以下报错:

The MySQL server at your-instance.rds.amazonaws.com:3306,
version 5.7.38-log - Source distribution, will now be checked for compatibility
issues for upgrade to MySQL 8.0.30...
Util.checkForServerUpgrade: This tool supports checking upgrade to MySQL server versions 8.0.11 to 8.0.21 (LogicError)

如果选择了 RDS MySQL 实例目标版本小于待检测实例的版本,具体报错如下:

The MySQL server at
your-instance.rds.amazonaws.com:3306, version
8.0.26 - Source distribution, will now be checked for compatibility issues for
upgrade to MySQL 8.0.25...
Util.checkForServerUpgrade: Target version must be greater than current version of the server (ArgumentError)

该工具检测项目与 RDS 检查项目存在一定的交集,在使用过程中只需关注出错项,部分不适用于 RDS 项不需过多关注,可以使用该工具做一个升级前的不兼容项预检查,以便提前修改不兼容项目。具体的以快照恢复并升级后的测试实例或者实际升级生产实例的输出 PrePatchCompatibility.log 为准。另外,需要注意的是 Amazon RDS MySQL 的 PrePatchCompatibility.log 中检测结果只有 Errors 或者 Warnings 总数的展示,并没有明确标识出哪一项是直接影响升级结果的 Error,哪一项是需要关注但是不直接影响升级结果的 Warning,所以在这篇博客中也将以 Amazon RDS MySQL 的 PrePatchCompatibility.log 实际输出为例做一个常见项具体的展开,以供大家在升级时参考。

3. Amazon RDS for MySQL升级检查结果分析

以版本 5.7.38 到 8.0.32 版本的升级检查项为例,RDS MySQL 一共有 26 项检查,检查结果分 Errors 和 Warnings。其中 Error 项即为可以直接导致升级失败的项目,Warning 则是不会导致升级失败但是需要在升级或后续的使用过程额外关注或修改的项目。接下来会对其中常见的 Error 以及 Warning 项进行详细的展开。

3.1 Error 检查项

以下列示了常见的能直接导致升级失败的检查项,如出现其他导致升级失败的项未列示的情况,可具体分析也可联系作者进一步讨论。以下各项摘取自升级时输出的 PrePatchCompatibility.log。

1) Usage of old temporal type
4) Table names in the mysql schema conflicting with new tables in 8.0
5) Partitioned tables using engines with non native partitioning
6) Foreign key constraint names longer than 64 characters
9) ENUM/SET column definitions containing elements longer than 255 characters
10) Usage of partitioned tables in shared tablespaces
13) Usage of removed GROUP BY ASC/DESC syntax
18) Issues reported by 'check table x for upgrade' command
19) The definer column for mysql.events cannot be null or blank
20) Tables with dangling FULLTEXT index reference

3.1.1 表包含 5.6.4 之前格式的旧时间列

报错信息

# PrePatchCompatibility.log
1) Usage of old temporal type
  Error: Following table columns use a deprecated and no longer supported
    timestamp disk storage format. They must be converted to the new format
    before upgrading. It can by done by rebuilding the table using 'ALTER TABLE
    <table_name> FORCE' command
  More information:
    https://mysqlserverteam.com/mysql-8-0-removing-support-for-old-temporal-datatypes/

  lff.lff.tt - timestamp /* 5.5 binary format */

说明

如果表包含 5.6.4 之前格式的旧时间列(TIME、 DATETIME 和 TIMESTAMP 不支持小数秒精度的列),则不支持就地升级到 MySQL 8.0。如果您的表仍然使用旧的临时列格式,请 REPAIR TABLE 在尝试就地升级到 MySQL 8.0 之前使用升级它们。在 5.6.4 之前,datetime 存储占用 8 个字节,而 timestamp 是占用 4 字节;但是在 5.6.4 之后,由于这两个类型允许有小数部分,所以占用的存储空间和以前不同;MySQL 规范规定,datetime 的非小数部分需要 5 个字节,而不是 8 个字节,而 timestamp 的非小数部分是需要 4 个字节,小数部分需要 0 到 3 个字节,具体取决于存储值的小数秒精度。所以如果 RDS MySQL 5.7 中遗留有旧格式的相关字段则会导致升级失败。

失败案例模拟

# 在 MySQL 5.5 上创建表并插入数据
CREATE TABLE `lff` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `dd` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=latin1
mysql> insert into lff(tt,dd) values(now(),now());
Query OK, 1 row affected (0.01 sec)
# 升级 MySQL 5.5 至 MySQL 5.6
/apps/svr/mysql56/bin/mysql_upgrade -P3306 -uroot -p -S /tmp/mysql3306.sock
# 升级 MySQL 5.6 至 MySQL 5.7
/apps/svr/mysql57/bin/mysql_upgrade -P3306 -uroot -p -S /tmp/mysql3306.sock --upgrade-system-tables                     
# 升级 MySQL 5.7 至 MySQL 8.0
ERROR

解决方案

mysql> check table lff;
+---------+-------+----------+---------------------------------------------------------------------------------------+
| Table   | Op    | Msg_type | Msg_text                                                                              |
+---------+-------+----------+---------------------------------------------------------------------------------------+
| lff.lff | check | error    | Table rebuild required. Please do "ALTER TABLE `lff` FORCE" or dump/reload to fix it! |
+---------+-------+----------+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE `lff` FORCE
    -> ;
Query OK, 2 rows affected, 1 warning (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 1

mysql> show Warnings;
+-------+------+-------------------------------------------------------------------------------------+
| Level | Code | Message                                                                             |
+-------+------+-------------------------------------------------------------------------------------+
| Note  | 1880 | TIME/TIMESTAMP/DATETIME columns of old format have been upgraded to the new format. |
+-------+------+-------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> check table lff;
+---------+-------+----------+----------+
| Table   | Op    | Msg_type | Msg_text |
+---------+-------+----------+----------+
| lff.lff | check | status   | OK       |
+---------+-------+----------+----------+
1 row in set (0.00 sec)

3.1.2 MySQL 系统数据库中不能有与 MySQL 8.0 数据字典中同名的表

报错信息

5) Table names in the mysql schema conflicting with new tables in 8.0
  Error: The following tables in mysql schema have names that will conflict
    with the ones introduced in 8.0 version. They must be renamed or removed
    before upgrading (use RENAME TABLE command). This may also entail changes to
    applications that use the affected tables.
  More information:
    https://dev.mysql.com/doc/refman/8.0/en/upgrading-strategies.html#upgrade-prerequisites

  mysql.catalogs - Table name used in mysql schema in 8.0

说明

MySQL 5.7 系统数据库 mysql 中不能有与 MySQL 8.0 数据字典使用的表同名的表。MySQL 8.0 中引入了事务数据字典(DD)支持,为此在 mysql 模式中创建了几个新的 DD 表。因此,在升级之前,应该删除或重命名 mysql 模式中具有冲突名称的用户表。注意 Amazon RDS MySQL 中不允许在 mysql 数据库中自行创建表,所以该模拟场景在开源 MySQL 实例中创建。

失败案例模拟

#MySQL5.7.38
mysql> use mysql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> create table catalogs(a int);
Query OK, 0 rows affected (0.01 sec)

解决方案

RENAME TABLE mysql.catalogs to lff.catalogs;
或者
DROP TABLE mysql.catalogs;

此外,也可以运行如下 SQL 查询在 5.7 中是否有数据字典表名冲突的情况。

SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE LOWER(TABLE_SCHEMA) = 'mysql' and LOWER(TABLE_NAME) IN ('catalogs','character_sets','check_constraints','collations','column_statistics','column_type_elements','columns','dd_properties','events','foreign_key_column_usage','foreign_keys','index_column_usage','index_partitions','index_stats','indexes','parameter_type_elements','parameters','resource_groups','routines','schemata','st_spatial_reference_systems','table_partition_values','table_partitions','table_stats','tables','tablespace_files','tablespaces','triggers','view_routine_usage','view_table_usage');

3.1.3 不能有使用不支持本地分区的引擎创建分区表

报错信息

# PrePatchCompatibility.log
5) Partitioned tables using engines with non native partitioning
    In MySQL 8.0 storage engine is responsible for providing its own partitioning handler, and the MySQL server no longer provides generic partitioning support. InnoDB and NDB are the only storage engines that provide a native partitioning handler that is supported in MySQL 8.0. A partitioned table using any other storage engine must be altered—either to convert it to InnoDB or NDB, or to remove its partitioning—before upgrading the server, else it cannot be used afterwards.
    More Information:
        https://dev.mysql.com/doc/refman/8.0/en/upgrading-from-previous-series.html#upgrade-configuration-changes

lff.p5_sub - MyISAM engine does not support native partitioning
lff.p5_sub_test - MyISAM engine does not support native partitioning

说明

官方文档明确支持不能有使用不支持原生分区的存储引擎的分区表。这一项在 MySQL 5.7 中被废弃,在 MySQL 8.0 中被移除,所以分区表使用不支持本地分区引擎的相关表需要在升级之前被修正。

失败案例模拟

#Amazon RDS MySQL5.7.38
mysql> use lff
Database changed
mysql> 
mysql> CREATE TABLE `p5_sub` (
    ->   `id` int(11) DEFAULT NULL,
    ->   `purchased` date DEFAULT NULL
    -> ) ENGINE=MyISAM DEFAULT CHARSET=latin1
    -> PARTITION BY RANGE ( YEAR(purchased))
    -> SUBPARTITION BY HASH ( TO_DAYS(purchased))
    -> SUBPARTITIONS 2
    -> (PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,
    ->  PARTITION p1 VALUES LESS THAN (2000) ENGINE = MyISAM,
    ->  PARTITION p2 VALUES LESS THAN MAXVALUE ENGINE = MyISAM);
Query OK, 0 rows affected (0.01 sec)

解决方案

ALTER TABLE table_name ENGINE = INNODB;
或者
ALTER TABLE table_name REMOVE PARTITIONING;

此外,也可以运行如下 SQL 查询在 5.7 中是否有不支持原生分区的存储引擎的分区表的存在。

SELECT TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE ENGINE NOT IN ('innodb', 'ndbcluster')
AND CREATE_OPTIONS LIKE '%partitioned%';

对于 Amazon RDS MySQL 需注意,Amazon RDS MySQL 8.0 版本中不允许创建 MyISAM 引擎类表,如创建,会显示 Warning 并自动转为 InnoDB 类表,具体信息如下。在 Amazon RDS MySQL 5.7 版本中可创建。

MySQL [lff]> create table a(a int) engine=myisam;
Query OK, 0 rows affected, 2 warnings (0.02 sec)

MySQL [lff]> show warnings\G
*************************** 1. row ***************************
  Level: Warning
   Code: 3161
Message: Storage engine MyISAM is disabled (Table creation is disallowed).
*************************** 2. row ***************************
  Level: Warning
   Code: 1266
Message: Using storage engine InnoDB for table 'a'
2 rows in set (0.00 sec)

MySQL [lff]> show create table a\G
*************************** 1. row ***************************
       Table: a
Create Table: CREATE TABLE `a` (
  `a` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

3.1.4 外键名不能超过 64 字符

报错信息

6) Foreign key constraint names longer than 64 characters
The following tables must be altered to have constraint names shorter than 64 characters (use ALTER TABLE).
More Information:
https://dev.mysql.com/doc/refman/8.0/en/upgrading-strategies.html#upgrade-prerequisites

lff.prod012345678901234567890123456789012345678901234567890123456789 - Foreign key longer than 64 characters

说明

MySQL 8.0 限制外键名称不能超过 64 个字符。在 8.0 版本之前,如果用户未明确指定外键名称,InnoDB 会自动生成外键约束名称,命名规则为:在表名后附加‘_ibfk_X’(其中 X 是数字)。如果表名本身占用 64 个字符,自动生成的外键约束名称会超过 64 个字符。所以在升级至 MySQL 8.0 版本时,会出现报错。

失败案例模拟

#Amazon RDS MySQL5.7.38
MySQL [lff]> CREATE TABLE `prod012345678901234567890123456789012345678901234567890123456789` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  `name` varchar(20) NOT NULL COMMENT 'product name', `price` float(10,3) NOT NULL,  `description` varchar(20) DEFAULT NULL, `count` int(11) NOT NULL DEFAULT '0', `sid` bigint(20) unsigned NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `sid_index` (`sid`) USING HASH) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.02 sec)

MySQL [lff]>  CREATE TABLE `sealer` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.03 sec)

MySQL [lff]> alter table prod012345678901234567890123456789012345678901234567890123456789 add   FOREIGN KEY (`sid`) REFERENCES `sealer` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [lff]> show create table prod012345678901234567890123456789012345678901234567890123456789\G
*************************** 1. row ***************************
       Table: prod012345678901234567890123456789012345678901234567890123456789
Create Table: CREATE TABLE `prod012345678901234567890123456789012345678901234567890123456789` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `sid` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_index` (`id`) USING HASH,
  UNIQUE KEY `sid_index` (`sid`) USING HASH,
  CONSTRAINT `prod012345678901234567890123456789012345678901234567890123456789_ibfk_1` FOREIGN KEY (`sid`) REFERENCES `sealer` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

解决方案

可以使用 ALTER TABLE 将其进行修改为不超过 64 字符的外键名。

MySQL [lff]> ALTER TABLE  prod012345678901234567890123456789012345678901234567890123456789 DROP FOREIGN KEY prod012345678901234567890123456789012345678901234567890123456789_ibfk_1,ADD CONSTRAINT ibfk_1 FOREIGN KEY (`sid`) REFERENCES `sealer` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [lff]> show create table prod012345678901234567890123456789012345678901234567890123456789\G
*************************** 1. row ***************************
       Table: prod012345678901234567890123456789012345678901234567890123456789
Create Table: CREATE TABLE `prod012345678901234567890123456789012345678901234567890123456789` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL COMMENT 'product name',
  `price` float(10,3) NOT NULL,
  `description` varchar(20) DEFAULT NULL,
  `count` int(11) NOT NULL DEFAULT '0',
  `sid` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `sid_index` (`sid`) USING HASH,
  CONSTRAINT `ibfk_1` FOREIGN KEY (`sid`) REFERENCES `sealer` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

此外,也可以运行如下 SQL 查询在 5.7 中名称长度超限的外键信息。

#Amazon RDS MySQL5.7
SELECT TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME IN
  (SELECT LEFT(SUBSTR(ID,INSTR(ID,'/')+1),
               INSTR(SUBSTR(ID,INSTR(ID,'/')+1),'_ibfk_')-1)
   FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN
   WHERE LENGTH(SUBSTR(ID,INSTR(ID,'/')+1))>64);

#Amazon RDS MySQL8.0
SELECT TABLE_SCHEMA, TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME IN
  (SELECT LEFT(SUBSTR(ID,INSTR(ID,'/')+1),
               INSTR(SUBSTR(ID,INSTR(ID,'/')+1),'_ibfk_')-1)
   FROM INFORMATION_SCHEMA.INNODB_FOREIGN
   WHERE LENGTH(SUBSTR(ID,INSTR(ID,'/')+1))>64);

3.1.5 表或者存储过程的 ENUM/SET 的所有元素总长度超过 255 字符,会导致升级失败

报错信息

# PrePatchCompatibility.log
9) ENUM/SET column definitions containing elements longer than 255 characters
The following columns are defined as either ENUM or SET and contain at least one element longer that 255 characters. They need to be altered so that all elements fit into the 255 characters limit.
More Information:
https://dev.mysql.com/doc/refman/8.0/en/string-type-overview.html

lff.t_long_enum.b - ENUM contains element longer than 255 characters

说明

MySQL 8.0 之前,所有枚举元素的总长度可能高达约 64k,因为它存储在 frm 文件中的 2 个字节中,因此表和存储过程可以使用大于 255 个字符的枚举元素创建。在 MySQL 8.0 中,则不支持包含太长枚举文字(即大于 255 个字符)。

失败案例模拟

#Amazon RDS MySQL5.7.38
mysql> show create table t_long_enum\G
*************************** 1. row ***************************
       Table: t_long_enum
Create Table: CREATE TABLE `t_long_enum` (
  `i` int(11) NOT NULL,
  `b` enum('\n         1         2         3         4         5         6         7         8         9         0\n                                                                                                   0\n                                                                                                   0') DEFAULT NULL,
  PRIMARY KEY (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
MySQL [lff]> select length(COLUMN_TYPE) from information_schema.columns where table_name='t_long_enum'\G
*************************** 1. row ***************************
length(COLUMN_TYPE): 7
*************************** 2. row ***************************
length(COLUMN_TYPE): 314
2 rows in set (0.00 sec)

解决方案

mysql> ALTER TABLE t_long_enum MODIFY b enum('\n 1 2 3 4 5 6 7 8 9 0\n');
Query OK, 0 rows affected (0.12 sec)
Records: 0 Duplicates: 0 Warnings: 0

还可以运行以下 SQL 在升级前查询是否有表或者存储过程的 ENUM 或者 SET 的所有元素总长度超过 255 字符。

select concat(TABLE_SCHEMA,".",TABLE_NAME,".",COLUMN_NAME) as schema_table_column from information_schema.columns where length(COLUMN_TYPE)>255+8 and TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA','SYS','PERFORMANCE_SCHEMA','MYSQL');

3.1.6 没有分区表在共享表空间

报错信息

# PrePatchCompatibility.log
10) Usage of partitioned tables in shared tablespaces
The following tables have partitions in shared tablespaces. Before upgrading to 8.0 they need to be moved to file-per-table tablespace. You can do this by running query like 'ALTER TABLE table_name REORGANIZE PARTITION X INTO (PARTITION X VALUES LESS THAN (30) TABLESPACE=innodb_file_per_table);'
More Information:
https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html#mysql-nutshell-removals

lff.indicators_v1_studentindicator - Partition p_district_id_7 is in shared tablespace innodb_system

说明

官方文档中明确表示在升级到 MySQL 8.0.13 或更高版本之前,共享 InnoDB 表空间中不能存在表分区,其中包括系统表空间和通用表空间。

失败案例模拟

#Amazon RDS MySQL5.7.38
MySQL [lff]> CREATE TABLE `indicators_v1_studentindicator` (
    ->        `id` int(11) NOT NULL AUTO_INCREMENT,
    ->        `created` datetime(6) NOT NULL,
    ->        `modified` datetime(6) NOT NULL,
    ->        `district_id` int(11) NOT NULL,
    ->        PRIMARY KEY (`id`,`district_id`)
    ->      ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
    ->      /*!50100 PARTITION BY LIST (`district_id`)
    ->      (PARTITION p_district_id_1 VALUES IN (1) TABLESPACE = `innodb_file_per_table` ENGINE = InnoDB,
    ->      PARTITION p_district_id_3 VALUES IN (3) TABLESPACE = `innodb_file_per_table` ENGINE = InnoDB,
    ->       PARTITION p_district_id_5 VALUES IN (5) TABLESPACE = `innodb_file_per_table` ENGINE = InnoDB,
    ->       PARTITION p_district_id_7 VALUES IN (7) TABLESPACE = `innodb_system` ENGINE = InnoDB) */;
Query OK, 0 rows affected, 1 warning (0.08 sec)

MySQL [lff]> 
MySQL [lff]> show warnings\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1681
Message: 'InnoDB : A table partition in a shared tablespace' is deprecated and will be removed in a future release.
1 row in set (0.00 sec)
MySQL [lff]> SELECT DISTINCT NAME, SPACE, SPACE_TYPE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES  WHERE NAME LIKE '%#P#%' AND SPACE_TYPE NOT LIKE 'Single'\G
*************************** 1. row ***************************
      NAME: lff/indicators_v1_studentindicator#P#p_district_id_7
     SPACE: 0
SPACE_TYPE: System
1 row in set (0.00 sec)1 row in set (0.00 sec)

还可以在升级前运行如下检查 SQL 查询共享 InnoDB 表空间中的表分区。

# From MySQL5.7
SELECT DISTINCT NAME, SPACE, SPACE_TYPE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES
  WHERE NAME LIKE '%#P#%' AND SPACE_TYPE NOT LIKE 'Single';
  
  
# From early MySQL8.0
 SELECT DISTINCT NAME, SPACE, SPACE_TYPE FROM INFORMATION_SCHEMA.INNODB_TABLES
  WHERE NAME LIKE '%#P#%' AND SPACE_TYPE NOT LIKE 'Single';

3.1.7 使用删除的 GROUP BY ASC/DESC 用法(SQL 用法类)

报错信息

# PrePatchCompatibility.log
13) Usage of removed GROUP BY ASC/DESC syntax
	The following DB objects use removed GROUP BY ASC/DESC syntax. They need to be altered so that ASC/DESC keyword is removed from GROUP BY clause and placed in appropriate ORDER BY clause.
	More Information:
		https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-13.html#mysqld-8-0-13-sql-syntax
lff.test_sql - PROCEDURE uses removed GROUP BY DESC syntax

说明

该类 SQL 用法在 MySQL 8.0 中已经被移除,使用会直接报错,有相关的存储过程存在,在升级时会直接导致升级失败。

失败案例模拟

#Amazon RDS MySQL5.7.38
MySQL [lff]> create table lff(id int,col varchar(20));
Query OK, 0 rows affected (0.02 sec)

MySQL [lff]> insert into lff values(1,'my'),(2,'you')
    -> ;
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

MySQL [lff]> select id,count(*) from lff group by id desc\G
*************************** 1. row ***************************
      id: 2
count(*): 1
*************************** 2. row ***************************
      id: 1
count(*): 1
2 rows in set, 1 warning (0.00 sec)

MySQL [lff]> show warnings\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1287
Message: 'GROUP BY with ASC/DESC' is deprecated and will be removed in a future release. Please use GROUP BY ... ORDER BY ... ASC/DESC instead
1 row in set (0.00 sec)

解决方案

使用 GROUP BY … ORDER BY … ASC/DESC 代替 ‘GROUP BY with ASC/DESC’。

3.1.8  Issues reported by ‘check table x for upgrade’ command

报错信息

18) Issues reported by 'check table x for upgrade' command
  Table (lff.lff) - Table rebuild required. Please do "ALTER TABLE
    `lff` FORCE" or dump/reload to fix it!

18) Issues reported by 'check table x for upgrade' command 
lff.Lff - Table 'lff.lff' doesn't exist

说明

该项检查会输出由’check table x for upgrade’命令检查出的问题,一般是 Error 类问题,需要我们手动处理,如上述 3.1.1 章节旧的时间格式导致的 Error 项以及由于 lower_case_table_names 参数设置在升级前后有变更,导致的表 check 失败的情况,也为 Error 项。因时间格式问题在 3.1.1 章节已做展开,这里不再赘述,而就 lower_case_table_names 变更导致升级失败这一案例进行展开。从 MySQL 8.0.19 开始,如果 lower_case_table_names 设置为 1,升级过程将检查表和架构名称,以确保所有字符均为小写。如果发现表或架构名称包含大写字符,表示在升级前你的实例 lower_case_table_names 设置为 0,那么升级过程将失败并出现错误。

失败案例模拟

# MySQL5.7.38
mysql> create table Lff(a int);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into Lff values(1);
Query OK, 1 row affected (0.01 sec)

mysql> show variables like '%lower_case_table_names%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| lower_case_table_names | 0     |
+------------------------+-------+
1 rows in set (0.00 sec)
# 在升级之前变更为 lower_case_table_names=1 设置

解决方案

# 在升级前,RENAME 相关表为全部小写或者 DROP 相关表。 
RENAME TABLE Lff to lff; 
DROP TABLE Lff;

可以运行以下检查 SQL 提前列出有大小写的对象,并进行 rename 或 drop 操作。

SELECT TABLE_NAME, if(sha(TABLE_NAME) !=sha(lower(TABLE_NAME)),'Yes','No') as UpperCase 
FROM information_schema.tables where sha(table_name)<>sha(lower(table_name)) and 
table_schema not in ('information_schema','sys','performance_schema','mysql'); 

SHOW VARIABLES LIKE 'lower_case_table_names';

注意 Amazon RDS MySQL 在升级的时候在参数修改这一块做了相应的限制。

3.1.9 mysql.events 列定义中不能出现 NULL 或者空

报错信息

19) The definer column for mysql.events cannot be null or blank.
	The definer column for mysql.events cannot be null or blank.
	More Information:
		https://dev.mysql.com/doc/refman/8.0/en/create-event.html and https://mysqlserverteam.com/upgrading-to-mysql-8-0-here-is-what-you-need-to-know/

test_db - Set definer column in mysql.event to a valid non-null definer.

说明

MySQL 创建 event 事件的 definer 列不能为空。

失败案例模拟

#Amazon RDS MySQL5.7.38
MySQL [test_db]> CREATE EVENT myevent ON SCHEDULE EVERY 1 HOUR DO UPDATE mytable SET column1 = column1 + 1 WHERE id = 1;
Query OK, 0 rows affected (0.01 sec)

MySQL [test_db]> UPDATE mysql.event SET definer=NULL WHERE name='myevent';
Query OK, 1 row affected, 1 warning (0.00 sec)

解决方案

ALTER DEFINER='neil'@'%' EVENT myevent COMMENT '';

创建和修改 event 事件的时候,确保 definer 值为非空。如遇到上述报错信息,可以运行以下检查 SQL 列出 definer 列为空的 event 事件,并进行修复。

MySQL [test_db]> SELECT definer,name FROM mysql.event;

3.1.10 存在悬空全文索引信息

报错信息

# PrePatchCompatibility.log
20) Tables with dangling FULLTEXT index reference
The following tables contain dangling FULLTEXT index which is not supported in major version 8.0. It is recommended to rebuild the table before upgrade. Please use this query to get the list of tables having dangling FULLTEXT index reference - SELECT INNODB_SYS_TABLES.NAME FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES JOIN INFORMATION_SCHEMA.INNODB_SYS_COLUMNS ON INNODB_SYS_TABLES.TABLE_ID = INNODB_SYS_COLUMNS.TABLE_ID WHERE INNODB_SYS_COLUMNS.NAME = 'FTS_DOC_ID'AND INNODB_SYS_TABLES.TABLE_ID NOT IN (SELECT TABLE_ID FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE TYPE = 32);
More Information:
https://dev.mysql.com/doc/refman/5.7/en/innodb-fulltext-index.html

Affected resource: lff/dangling_fulltext_index - This table contains dangling FULLTEXT index. Kindly recreate the table before upgrade.

说明

InnoDB 使用称为文档 ID (DOC_ID) 的唯一文档标识符将全文索引中的单词映射到该单词出现的文档记录。该映射需要索引表上的 FTS_DOC_ID 列。如果未定义 FTS_DOC_ID 列,InnoDB 在创建全文索引时会自动添加隐藏的 FTS_DOC_ID 列。当表中添加过全文索引后进行删除,则会有对应的 FTS_DOC_ID 列信息,会导致升级失败。

失败案例模拟

#Amazon RDS MySQL5.7.38
MySQL [lff]> CREATE TABLE dangling_fulltext_index
    ->      (id INT AUTO_INCREMENT PRIMARY KEY, txtcol TEXT NOT NULL)
    ->       ENGINE=InnoDB;
Query OK, 0 rows affected (0.02 sec)

MySQL [lff]> ALTER TABLE dangling_fulltext_index ADD FULLTEXT(txtcol);
Query OK, 0 rows affected, 1 warning (0.20 sec)
Records: 0  Duplicates: 0  Warnings: 1

MySQL [lff]> show warnings;
+---------+------+--------------------------------------------------+
| Level   | Code | Message                                          |
+---------+------+--------------------------------------------------+
| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)

MySQL [lff]> ALTER TABLE dangling_fulltext_index DROP INDEX txtcol;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [lff]> show create table dangling_fulltext_index\G
*************************** 1. row ***************************
       Table: dangling_fulltext_index
Create Table: CREATE TABLE `dangling_fulltext_index` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `txtcol` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

解决方案

Optimize table lff.dangling_fulltext_index;

还可以运行如下检查语句提前发现悬空全文索引信息。

SELECT INNODB_SYS_TABLES.NAME FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES JOIN INFORMATION_SCHEMA.INNODB_SYS_COLUMNS ON INNODB_SYS_TABLES.TABLE_ID = INNODB_SYS_COLUMNS.TABLE_ID WHERE INNODB_SYS_COLUMNS.NAME = 'FTS_DOC_ID'AND INNODB_SYS_TABLES.TABLE_ID NOT IN (SELECT TABLE_ID FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES WHERE TYPE = 32);

3.1.11 视图列名不能超过 64 字符

报错信息

# Error.log
2023-06-15T11:38:20.726131Z 2 [ERROR] [MY-013140] [Server] Incorrect column name 'abcde012345678901234567890123456789012345678901234567890123456789'
2023-06-15T11:38:20.726152Z 2 [ERROR] [MY-011081] [Server] Upgrade of view 'lff.test_view' failed. Re-create the view with the explicit column name lesser than 64 characters.
2023-06-15T11:38:20.731508Z 2 [ERROR] [MY-010023] [Server] Error in Creating View lff.test_view
#Event提示
The DB instance is in a state that can't be upgraded. RDS detected incompatibilities when attempting to upgrade to MySQL 8.0.28.R4. For more information, view the details in the upgradeFailure.log file, accessible in the 'logs' section below. Resolve the issue before upgrading to MySQL 8.0.28.R4. For more information about upgrading to MySQL 8.0, see the following documents: https://dev.mysql.com/doc/refman/8.0/en/upgrade-prerequisites.html and https://mysqlserverteam.com/upgrading-to-mysql-8-0-here-is-what-you-need-to-know/.

说明

在 MySQL 8.0 之前,用户可以创建具有最多 255 个字符的显式列名的视图。为遵守列名的最大长度,MySQL 8.0 不支持显式列名超过 64 个字符的视图。目前这些视图只能通过在 MySQL 5.7 中执行 SHOW CREATE VIEW 来识别。所以在升级至 MySQL 8.0 之前需要修正超长列名,避免升级时候的出错。需要注意的是,这一项在 PrePatchCompatibility 阶段无法检查,但是会导致升级失败,具体的信息可以在 error/mysql-error.log 中查看。

失败案例模拟

#Amazon RDS MySQL5.7.38
MySQL [lff]> CREATE VIEW test_view(abcde012345678901234567890123456789012345678901234567890123456789) AS  SELECT 1;
Query OK, 0 rows affected (0.02 sec)

解决方案

RENAME TABLE original_view_name TO new_view_name;

还可以运行如下检查语句查询对应项,这里需注意大于 64 字符的视图列名在 INFORMATION_SCHEMA.COLUMNS 中会出现截断情况,所以如果升级出现该错误,可进一步检查列名为 64 字符的 VIEW,具体查看 view 定义确认 Error 项。

SELECT c.TABLE_SCHEMA,c.TABLE_NAME,c.COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS c JOIN INFORMATION_SCHEMA.TABLES t ON c.TABLE_NAME=t.TABLE_NAME and c.TABLE_SCHEMA=t.TABLE_SCHEMA where c.TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA','SYS','PERFORMANCE_SCHEMA','MYSQL') AND t.TABLE_COMMENT='VIEW' AND length(c.COLUMN_NAME)>=64;

3.2 Warnings 检查项

不会直接导致升级失败但是在新版本使用前或者使用中需要注意或者修正的检查项。接下来对常见的以下几项进行详细的展开。

2) Usage of db objects with names conflicting with new reserved keywords
3) Usage of utf8mb3 charset
24) Creating indexes larger than 767 bytes on tables with redundant row format might cause the tables to be inaccessible. 

3.2.1 新增关键字

警示信息

3) Usage of db objects with names conflicting with new reserved keywords
  The following objects have names that conflict with new reserved
    keywords. Ensure queries sent by your applications use `quotes` when
    referring to them or they will result in errors.
  More information:
    https://dev.mysql.com/doc/refman/en/keywords.html

  lff.lff.lead - Column name

说明

MySQL 8.0 新增关键字有 cume_dist、dense_rank、empty、except、first_value、grouping、groups、json_table、lag、last_value、lateral、lead、nth_value、ntile、of、over、percent_rank、rank、recursive、row_number、system、window 关键字范围分布在数据库、表、索引、列、alias、view、存储过程、分区、表空间。

为避免使用相关对象的过程中出现问题,在升级前需注意相关对象关键字使用的情况,如有,需要对照使用方法调整代码或存储过程。

案例模拟

# Amazon RDS MySQL5.7.38
# 关键字 lead 未做处理,则创建表报错
MySQL [lff]> CREATE TABLE lff (a int DEFAULT NULL,lead int DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;                       
Query OK, 0 rows affected (0.04 sec)

解决方案

设置 sql_model   SET sql_mode = 'ANSI_QUOTES' 设置此模式后,可以加上双引号使用。

或者直接加上撇号,键盘数字 1 左边的键。

mysql> CREATE TABLE lff3 (  a int(11) DEFAULT NULL,  json_table int(11) DEFAULT NULL,  lag int(11) DEFAULT NULL,  lead int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
    Query OK, 0 rows affected (0.02 sec)
    
mysql> show variables like '%sql_mode%';
+---------------+-------------+
| Variable_name | Value       |
+---------------+-------------+
| sql_mode      | ANSI_QUOTES |
+---------------+-------------+
1 row in set (0.01 sec)

mysql> insert into lff3 (`lag`) values(11);
Query OK, 1 row affected (0.00 sec)

mysql> insert into lff3 ("lag") values(11);
Query OK, 1 row affected (0.01 sec)

mysql> set sql_mode="";
Query OK, 0 rows affected (0.00 sec)

mysql> insert into lff3 ("lag") values(11);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"lag") values(11)' at line 1
mysql> insert into lff3 (`lag`) values(11);
Query OK, 1 row affected (0.00 sec) 

可以使用如下 SQL 在升级之前进行预检查。

select concat(TABLE_SCHEMA,".",TABLE_NAME) from information_schema.tables where TABLE_NAME in ('cume_dist','dense_rank','empty','except','first_value','grouping','groups','json_table','lag','last_value','lateral','lead','nth_value','ntile、of','over','percent_rank 、rank','recursive','row_number','system','window') and TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA','SYS','PERFORMANCE_SCHEMA','MYSQL');

3.2.2 字符集问题

警示信息

# PrePatchCompatibility.log
3) Usage of utf8mb3 charset
The following objects use the utf8mb3 character set. It is recommended to convert them to use utf8mb4 instead, for improved Unicode support.
More Information:
https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-utf8mb3.html

mysql - schema's default character set: utf8
lff.b_innodb.keyname - column's default character set: utf8
lff.b_innodb.keyval - column's default character set: utf8

说明

MySQL 8.0 默认字符集为 UTF8MB4,MySQL 5.7 默认字符集为 UTF8,MySQL 5.7 使用 UTF8MB3(UTF8) 1-3 字节存储一个字符,MySQL 8.0 默认UTF8MB4 1-4 字节存储一个字符,所以对于列类型的长度的会存在一定的影响,比如 TINYTEXT 最大存储 255 字节,那么可能会出现在 5.7 中最大字符数为 85,8.0 最大字符数则为 63。同样对于索引长度也会有影响,在 REDUNDANT 或者 COMPACT 模式下,最大索引长度为 767 字节,那在 5.7 则允许最长字符字段的索引 255 字符,在 8.0 则是 191 字符。对于升级后未变更字符集的 UTF8 字符类表,如果升级后 character_set_database 设置为默认 utf8mb4,新建表在没有指定 CHARACTER SET 时默认使用 UTF8MB4,当新表和迁移表相关字符字段发生 JOIN 时,会因为 JOIN 两端字段字符集类型不一致导致相关条件字段不能使用索引。

在 Amazon RDS MySQL 8.0 中 UTF8MB3 在表创建的时候会有 Warnings,但是修改还是有一定的代价,需要结合具体使用场景需求决定。且后期 UTF8MB3 会被移除,建议修改 UTF8MB3 类表为 UTF8MB4。

案例模拟

#RDS MySQL 5.7.38
MySQL [lff]> CREATE TABLE `b_innodb1` (     `id` int(10) NOT NULL AUTO_INCREMENT primary key,  `keyname` char(20) DEFAULT NULL,  `keyval` varchar(1000))  ENGINE=innodb AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.04 sec)

解决方案

# 这种修改方式会修改已有字符类字段,会阻塞 DML 操作
# 数据库
ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
# 表
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 字段
ALTER TABLE table_name CHANGE column_name column_name VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

# 这种修改方式不会修改已存在的字段类型,只对后续加入的字段有约束,不阻塞 DML 操作
ALTER DATABASE 数据库名
[[DEFAULT] CHARACTER SET 字符集名称]
[[DEFAULT] COLLATE 比较规则名称];
ALTER TABLE 表名
[[DEFAULT] CHARACTER SET 字符集名称]
[COLLATE 比较规则名称]; 
ALTER TABLE 表名 MODIFY 列名字符串类型 [CHARACTER SET 字符集名称] [COLLATE 比较规则名称];

也可以使用如下检查 SQL 进行预先检查与发现。

select concat(TABLE_SCHEMA,".",TABLE_NAME,".",COLUMN_NAME)  from information_schema.columns where  TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA','SYS','PERFORMANCE_SCHEMA','MYSQL') and CHARACTER_SET_NAME='utf8';

Amazon RDS MySQL 8.0.38 中使用 utf8 会转换 utf8mb3,且 utf8 和 utf8mb3 均会有 Warnings。因为 utf8 这个别名在后续版本中将会成为 utf8mb4 的别名,且 utf8mb3 在后续版本中会移除。且 5.7.38 版本创建表的时候如指定 DEFAULT CHARSET=utf8,该表升级到 8.0.32后,表字符集会自动转换为 CHARSET=utf8mb3。并且在 MySQL8.0 中将不再允许 utf8 这种写法,统一为 utf8mb3。

MySQL [lff]> CREATE TABLE `product` (   `id` bigint unsigned NOT NULL AUTO_INCREMENT,   `name` varchar(20) NOT NULL COMMENT "product name",UNIQUE KEY `id_index` (`id`))ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
Query OK, 0 rows affected, 1 warning (0.02 sec)

MySQL [lff]> show warnings\G
*************************** 1. row ***************************
  Level: Warning
   Code: 1287
Message: 'utf8mb3' is deprecated and will be removed in a future release. Please use utf8mb4 instead
1 row in set (0.00 sec)

MySQL [lff]> CREATE TABLE `product1` (   `id` bigint unsigned NOT NULL AUTO_INCREMENT,   `name` varchar(20) NOT NULL COMMENT "product name",UNIQUE KEY `id_index` (`id`))ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 1 warning (0.02 sec)

MySQL [lff]> show warnings\G
  Level: Warning
   Code: 3719
Message: 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. 
1 row in set (0.00 sec)

3.2.3 索引长度不能超过 767 字节

警示信息

# PrePatchCompatibility.log
23) Creating indexes larger than 767 bytes on tables with redundant row format might cause the tables to be inaccessible.
    You have one or more tables with redundant row format. Creating an index larger than 767 bytes might cause the table to be inaccessible (only 50 tables are shown).
    More Information:
        https://bugs.mysql.com/bug.php?id=99791
[table_schema, table_name]
lff,raw_log_meta_data1 - Consider restoring the DB instance from a snapshot creating before upgrading, and change the row_format of the tables to dynamic.

说明

在 MySQL 5.5 版本,引入了 innodb_large_prefix,用来禁用大型前缀索引,以便与不支持大索引键前缀的早期版本的 InnoDB 兼容。开启 innodb_large_prefix 可以使单索引的长度限制达到 3072 字节(但是联合索引总长度限制还是 3072 字节),禁用时单索引的长度限制为 767 字节在 MySQL 5.5 版本与 MySQL 5.6 版本,innodb_large_prefix 是默认关闭的,在 MySQL 5.7 及以上版本则默认开启。在 MySQL 8.0 版本中,innodb_large_prefix 已被移除,从版本 8.0 开始,索引长度限制由表字段(row format)决定,若为 DYNAMIC 或 COMPRESSED 时,限制值为 3072;为 REDUNDANT 或 COMPACT 时,限制值为 767。升级时候会检查 REDUNDANT 或 COMPACT 行格式的表升级至 MySQL 8.0 后,表索引长度会超过 767 字节的表。

案例模拟

# MySQL 5.7.38
# 以下表结构中的 file_path 在升级到 MySQL 8.0 之后,因为默认字符集为 utf8mb4,一个字符最多可占 4 字节,而 200*4>767,出现上述不兼容 warning 项
MySQL [lff]> show create table raw_log_meta_data1\G
*************************** 1. row ***************************
       Table: raw_log_meta_data1
Create Table: CREATE TABLE `raw_log_meta_data1` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `file_path` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `file_path` (`file_path`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT
1 row in set (0.00 sec)

# MySQL 8.0.32
# 添加长度大于 767 字节的索引,示例中表字符集为 utf8mb3,则一个字符最大可占用 3 字节,所以 varchar(256)字段最大占用长度为 768 字节,超出 767 字节限制,会对该索引设置进行字段长度截取操作。

MySQL [lff]> alter table raw_log_meta_data1 add column a varchar(256);
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [lff]> alter table raw_log_meta_data1 add index idx_a(a);
Query OK, 0 rows affected, 1 warning (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 1

MySQL [lff]> show warnings;
+---------+------+---------------------------------------------------------+
| Level   | Code | Message                                                 |
+---------+------+---------------------------------------------------------+
| Warning | 1071 | Specified key was too long; max key length is 767 bytes |
+---------+------+---------------------------------------------------------+
1 row in set (0.00 sec)
# 索引进行了截断
MySQL [lff]> show create table raw_log_meta_data1\G
*************************** 1. row ***************************
       Table: raw_log_meta_data1
Create Table: CREATE TABLE `raw_log_meta_data1` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `file_path` varchar(200) DEFAULT NULL,
  `a` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `file_path` (`file_path`),
  KEY `idx_a` (`a`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT
1 row in set (0.00 sec)

解决方案

如果表默认字符集保持 MySQL 5.7 中不变,则可以不对该 Warning 进行额外处理。如果需变更表字符集为 utf8mb4,则需要先处理该表索引字段超长问题。可以通过转换表的存储模式来增大限制值。

#MySQL8.0.32
MySQL [lff]> ALTER TABLE raw_log_meta_data1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ERROR 3886 (HY000): Could not change column 'file_path' of table 'raw_log_meta_data1'. The resulting size of index 'file_path' would exceed the max key length of 767 bytes.
MySQL [lff]> alter table raw_log_meta_data1 ROW_FORMAT=dynamic;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

MySQL [lff]> ALTER TABLE raw_log_meta_data1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

4.其他未展开项目

除以上常见项,还有以下 13 项仅根据提示修正即可的升级检查项列示如下。如出现且不能根据提示直接解决,可联系作者进一步讨论。

7)Usage of obsolete MAXDB sql_mode flag

8)Usage of obsolete sql_mode flags

11)Circular directory references in tablespace data file paths

12)Usage of removed functions

14)Removed system variables for error logging to the system log configuration

15)Removed system variables

16)System variables with new default values

17)Schema inconsistencies resulting from file removal or corruption

21)Routines with deprecated keywords in definition

22)DB instance must have enough free disk space

23)MySQL preupgrade check to catch users created with MYSQL_NATIVE_PASSWORD plugin

25)The tables with redundant row format can’t have an index larger than 767 bytes.

26)Column definition mismatch between InnoDB Data Dictionary and actual table definition.

5.总结

Amazon RDS for MySQL 5.7 到 8.0 的升级非常重要,其中大版本升级涉及到的兼容性也是用户非常关注的一部分,本篇博客对兼容性检查中常见项目做了具体的展开和说明,并且对可用于兼容性检查工具 MySQL Shell 中的检查函数 util.checkForServerUpgrade()的使用方法以及注意事项作了相关示例,希望能帮助您成功升级 Amazon RDS for MySQL 5.7 到 8.0。

系列博客

本篇作者

李芬芳

亚马逊云科技数据库专家,负责亚马逊云科技数据库相关的架构优化、成本管理、技术咨询等工作。加入 AWS 之前曾就职于腾讯、唯品会、圆通速递等公司,有多年数据库管理经验。

刘营宇

西云数据解决方案架构师,10+年软件架构设计、研发经验。对系统架构、云计算技术有丰富的经验。

李宁

西云数据技术客户经理,负责为亚马逊云科技中国客户提供企业级技术支持和专业指导。曾就职于全球领先的数据库公司。在高并发、高可用数据库架构设计,数据处理以及数据库管理和性能优化等方面具有丰富的经验。

马丽丽

亚马逊云科技数据库解决方案架构师,十余年数据库行业经验,先后涉猎 NoSQL 数据库 Hadoop/Hive、企业级数据库 DB2、分布式数仓 Greenplum/Apache HAWQ 以及亚马逊云原生数据库的开发和研究。

张凯

AWS 解决方案架构师,主要负责基于 AWS 云计算的解决方案架构设计和的方案咨询;擅长的行业有教育、物流、电商等行业,加入前曾在顺丰、京东任职研发管理和项目管理等职务,具有多年的架构设计、项目管理经验。

肖福生

西云数据解决方案架构师,负责西云数据华南区域的云计算方案咨询和设计,同时致力于亚马逊云科技在国内的应用和推广。在加入西云数据之前,在云计算行业担任解决方案架构师,负责互联网行业的云计算方案咨询以及架构设计,在企业软件架构,数据库,中间件软件等方面有丰富的经验。