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='';

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
19天前
|
关系型数据库 MySQL Linux
升级到MySQL 8.4,MySQL启动报错:io_setup() failed with EAGAIN
当MySQL 8.4启动时报错“io_setup() failed with EAGAIN”时,通常是由于系统AIO资源不足所致。通过增加AIO上下文数量、调整MySQL配置、优化系统资源或升级内核版本,可以有效解决这一问题。上述解决方案详细且实用,能够帮助管理员快速定位并处理此类问题,确保数据库系统的正常运行。
61 9
|
2月前
|
NoSQL 安全 关系型数据库
2024Mysql And Redis基础与进阶操作系列(6)作者——LJS[含MySQL 多表之一对一/多;多对多;多表联合查询等详解步骤及常见报错问题所对应的解决方法]
MySQL 多表之一对一/多;多对多;多表联合之交叉连接;内连接;左、右、外、满、连接;子查询及关键字;自连接查询等详解步骤及常见报错问题所对应的解决方法
|
2月前
|
存储 SQL NoSQL
|
2月前
|
NoSQL 关系型数据库 MySQL
2024Mysql And Redis基础与进阶操作系列(8)作者——LJS[含MySQL 创建、修改、跟新、重命名、删除视图等具体详步骤;注意点及常见报错问题所对应的解决方法]
MySQL 创建、修改、跟新、重命名、删除视图等具体详步骤;举例说明注意点及常见报错问题所对应的解决方法
|
2月前
|
SQL NoSQL 关系型数据库
|
12天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
39 3
|
12天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
42 3
|
12天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
54 2
|
26天前
|
关系型数据库 MySQL 数据库
Python处理数据库:MySQL与SQLite详解 | python小知识
本文详细介绍了如何使用Python操作MySQL和SQLite数据库,包括安装必要的库、连接数据库、执行增删改查等基本操作,适合初学者快速上手。
180 15
|
19天前
|
SQL 关系型数据库 MySQL
数据库数据恢复—Mysql数据库表记录丢失的数据恢复方案
Mysql数据库故障: Mysql数据库表记录丢失。 Mysql数据库故障表现: 1、Mysql数据库表中无任何数据或只有部分数据。 2、客户端无法查询到完整的信息。