MySQL 报错 ERROR 1709: Index column size too large

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL 报错 ERROR 1709: Index column size too large

背景

最近同事突然找到我说测试环境中有张表无法访问,SELECT、DML 和 DDL 执行均报错 ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.

其实看到 767 这个数字,大家可能会猜想这估计和 compact/redundant 行格式有关系,后续也确实证实了和这个有点关系。

问题发生了就要想办法处理,当时第一反应是能不能有些 “特殊操作” 调整一下元数据,但能力有限无法实现。由于是测试环境,数据没那么重要,而且还是单节点,后续处理无非是利用备份重做这套库;若不想重做,而且该表不重要,也可以直接废弃该表,但是 xtrabackup 备份可能会报错。

既然问题一旦发生,只能通过备份恢复来解决,那么我们应该探究一下如何提前避免该问题。

原因探究

以下为测试环境复现过程:

MySQL 5.6.21 原地升级至 5 .7.20

先调整数据库配置文件,以下为简要升级步骤:

shell>/mysql/mysql-5.7.20/bin/mysqld_safe ... &
shell>/mysql/mysql-5.7.20/bin/mysql_upgrade ...
mysql>shutdown;
shell>/mysql/mysql-5.7.20/bin/mysqld_safe ... &

MySQL 5.7.20 原地升级至 8.0.21

先调整数据库配置文件,以下为简要升级步骤:

mysql>/mysql/mysql-8.0.21/bin/mysqld_safe ... &
mysql>shutdown;
shell>/mysql/mysql-8.0.21/bin/mysqld_safe ... &

8.0.21 数据库添加字段并添加索引

表默认字符集为 utf8

mysql> alter table sky.test add column test_col varchar(500);
Query OK, 0 rows affected (10.09 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> alter table sky.test add index idx_test_col(test_col);   
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

正常情况下,这个索引理应无法创建成功,会立即抛出错误 ERROR 1071 (42000):Specified key was too long; max key length is 767 bytes。当然一方面原因是 MySQL 5.7 及 8.0 默认行格式为 dynamic,另一方面即使显式指定 row_format=compact,也会立即抛出错误。示例如下:

mysql>create table sky1 (id int);
Query OK, 0 rows affected (0.05 sec)
mysql>alter table sky1 add column test_col varchar(500);   
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql>alter table sky1  add index idx_test_col(test_col); 
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql>create table sky2(id int) row_format=compact;
Query OK, 0 rows affected (0.06 sec)
mysql>alter table sky2 add column test_col varchar(500); 
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql>alter table sky2 add index idx_test_col(test_col);
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

数据库重启前,该表可正常访问。

重启数据库

systemctl stop mysqld_3306
systemctl start mysqld_3306

查看表情况

mysql> select *from sky.test limit 1;
ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
mysql> alter table sky.test row_format=dynamic;
ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
mysql> alter table sky.test engine=innodb;
ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
mysql> check table sky.test ;
+------------------+--------+----------+--------------------------------------------------------------------+
| Table            | Op     | Msg_type | Msg_text                                                           |
+------------------+--------+----------+--------------------------------------------------------------------+
| sky.test         | check  | Error    | Index column size too large. The maximum column size is 767 bytes. |
| sky.test         | check  | Error    | Table 'sky.test' doesn't exist                                     |
| sky.test         | check  | error    | Corrupt                                                            |
+------------------+--------+----------+--------------------------------------------------------------------+
3 rows in set (0.01 sec)

查看相关信息

mysql>select TABLE_SCHEMA,TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS from information_schema.tables where table_schema='sky';
+--------------+------------+------------+--------------------+
| TABLE_SCHEMA | TABLE_NAME | ROW_FORMAT | CREATE_OPTIONS     |
+--------------+------------+------------+--------------------+
| sky          | test       | Compact    |                    |
| sky          | sky1       | Dynamic    |                    |
| sky          | sky2       | Compact    | row_format=COMPACT |
+--------------+------------+------------+--------------------+

找不同的粗略猜想

sky2 表比 test 多了一个 create_options 选项,所以不会触发 bug。而且 create_options 是建表时显式指定的行格式 compact,而 test 表是在 5.6 版本隐式创建的行格式 compact;8.0 默认创建表的行格式为 Dynamic(由 innodb_default_row_format 参数控制),Dynamic 行格式不会存在 767bytes 的限制。

碰到这样奇奇怪怪的问题,第一反应就是不走运碰到了 bug,因此先去 bug 库中搜索一番,果不其然搜到了 Bug #99791,与我们测试环境的情况极为类似。

Bug #99791 中表明官方在 MySQL 8.0.22 版本修复了 非显式定义的 redundant 行格式表允许创建的索引列大小超 767 bytes 的 bug。实际上笔者在测试环境验证了一下 MySQL 8.0.22 确实已解决该问题,即隐式创建的 compact 行格式表在待创建的索引列超 767bytes 时直接返回错误 ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes。因此猜想虽然该 bug 行格式与笔者本次环境对不上,但应该解决的是同一个问题,都是为了解决因隐式定义 compact/redundant 行格式而导致的问题。

解决方案

综上所述,我们可以得出以下解决方案:

  1. MySQL 5.6 升级至 MySQL 8.0.21 时,避免使用原地升级的方案,可新建一个 MySQL 8.0.21 的环境,将数据逻辑导入并搭建复制关系;若 8.0.21 环境设置 innodb_default_row_format=Dynamic 参数,在逻辑导入 / 复制时新环境会自动将行格式转为 Dynamic。
  2. 升级时选择高于 MySQL 8.0.21 版本的数据库,避免触发该 bug。
  3. 若当前已经存在 MySQL 5.6 原地升级至 MySQL 8.0.21 的环境。
  • 可通过以下 SQL 语句排查是否存在超过 767bytes 的问题表;若存在,可以趁现在数据库未重启,改造涉及的索引。
select s.table_schema,s.table_name,s.index_name,s.column_name from information_schema.statistics s,information_schema.columns c,information_schema.tables i where s.table_name=c.table_name and s.table_schema=c.table_schema and c.column_name=s.column_name and s.table_name=i.table_name and s.table_schema=i.table_schema and i.row_format in ('Redundant','Compact') and (s.sub_part is null or s.sub_part>255) and c.character_octet_length >767;
  • 筛选出隐式创建行格式为 compact/redundant 的表,并显式指定,如 alter table xx row_format=dynamic/compact 。相关 SQL 如下:
select TABLE_SCHEMA,TABLE_NAME,ROW_FORMAT,CREATE_OPTIONS from information_schema.tables where ROW_FORMAT in ('Compact','Redundant') and CREATE_OPTIONS='';

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
17天前
|
SQL DataWorks 关系型数据库
DataWorks操作报错合集之如何处理数据同步时(mysql->hive)报:Render instance failed
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
18天前
|
SQL 关系型数据库 MySQL
实时数仓 Hologres操作报错合集之Flink CTAS Source(Mysql) 表字段从可空改为非空的原因是什么
实时数仓Hologres是阿里云推出的一款高性能、实时分析的数据库服务,专为大数据分析和复杂查询场景设计。使用Hologres,企业能够打破传统数据仓库的延迟瓶颈,实现数据到决策的无缝衔接,加速业务创新和响应速度。以下是Hologres产品的一些典型使用场景合集。
|
11天前
|
关系型数据库 MySQL Java
【Azure 应用服务】应用服务连接 Azure MySQL 一直失败,报错 Create connection error
【Azure 应用服务】应用服务连接 Azure MySQL 一直失败,报错 Create connection error
|
12天前
|
关系型数据库 MySQL Java
【Azure 应用服务】App Service 无法连接到Azure MySQL服务,报错:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
【Azure 应用服务】App Service 无法连接到Azure MySQL服务,报错:com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
115 0
|
21天前
|
关系型数据库 MySQL 数据库连接
UiPath 连接 Mysql 报错: 在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
UiPath 连接 Mysql 报错: 在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
|
14天前
|
SQL 关系型数据库 MySQL
【揭秘】MySQL binlog日志与GTID:如何让数据库备份恢复变得轻松简单?
【8月更文挑战第22天】MySQL的binlog日志记录数据变更,用于恢复、复制和点恢复;GTID为每笔事务分配唯一ID,简化复制和恢复流程。开启binlog和GTID后,可通过`mysqldump`进行逻辑备份,包含binlog位置信息,或用`xtrabackup`做物理备份。恢复时,使用`mysql`命令执行备份文件,或通过`innobackupex`恢复物理备份。GTID模式下的主从复制配置更简便。
62 2
|
9天前
|
弹性计算 关系型数据库 数据库
手把手带你从自建 MySQL 迁移到云数据库,一步就能脱胎换骨
阿里云瑶池数据库来开课啦!自建数据库迁移至云数据库 RDS原来只要一步操作就能搞定!点击阅读原文完成实验就可获得一本日历哦~
|
13天前
|
关系型数据库 MySQL 数据库
RDS MySQL灾备服务协同解决方案构建问题之数据库备份数据的云上云下迁移如何解决
RDS MySQL灾备服务协同解决方案构建问题之数据库备份数据的云上云下迁移如何解决
|
10天前
|
人工智能 小程序 关系型数据库
【MySQL】黑悟空都掌握的技能,数据库隔离级别全攻略
本文以热门游戏《黑神话:悟空》为契机,深入浅出地解析了数据库事务的四种隔离级别:读未提交、读已提交、可重复读和串行化。通过具体示例,展示了不同隔离级别下的事务行为差异及可能遇到的问题,如脏读、不可重复读和幻读等。此外,还介绍了在MySQL中设置隔离级别的方法,包括全局和会话级别的调整,并通过实操演示了各隔离级别下的具体效果。本文旨在帮助开发者更好地理解和运用事务隔离级别,以提升数据库应用的一致性和性能。
71 2
【MySQL】黑悟空都掌握的技能,数据库隔离级别全攻略
|
15天前
|
数据可视化 关系型数据库 MySQL
Mysql8 如何在 Window11系统下完成跳过密钥校验、完成数据库密码的修改?
这篇文章介绍了如何在Windows 11系统下跳过MySQL 8的密钥校验,并通过命令行修改root用户的密码。
Mysql8 如何在 Window11系统下完成跳过密钥校验、完成数据库密码的修改?

热门文章

最新文章

推荐镜像

更多
下一篇
DDNS