暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

记录一则处理ERROR 1071 (42000)索引过长的问题

IT那活儿 2021-04-26
1992

发现问题

最近业务那边反馈他们执行修改字段长的操作时,有如下报错:

mysql>Alter table tablename modify column 'clumnname' varchar(500) not null;ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

分析问题

由报错可知是索引的长度达到了限制,经过查阅资料,在官方文档中找到了如下解释:
  • 8.0版本:

  • 5.7版本:

If innodb_large_prefix is enabled (the default), the index key prefix limit is 3072 bytes for InnoDB tables that use DYNAMIC or COMPRESSED row format. If innodb_large_prefix is disabled, the index key prefix limit is 767 bytes for tables of any row format.
 
innodb_large_prefix is deprecated and will be removed in a future release. innodb_large_prefix was introduced in MySQL 5.5 to disable large index key prefixes for compatibility with earlier versions of InnoDB that do not support large index key prefixes.
 
The index key prefix length limit is 767 bytes for InnoDB tables that use the REDUNDANT or COMPACT row format. For example, you might hit this limit with a column prefix index of more than 255 characters on a TEXT or VARCHAR column, assuming a utf8mb3 character set and the maximum of 3 bytes for each character.
 
Attempting to use an index key prefix length that exceeds the limit returns an error. To avoid such errors in replication configurations, avoid enablinginnodb_large_prefix on the master if it cannot also be enabled on slaves.
 
The limits that apply to index key prefixes also apply to full-column index keys.

单列索引长度限制

上面的意思是说如果启用了索引前缀参数(innodb_large_prefix ),那么InnoDB 存储引擎中的表的行格式为DYNAMIC or COMPRESSED时,prefixes的长度限制长度的最大限制为3072 bytes,关闭了索引前缀参数(innodb_large_prefix )时,prefixes的长度限制长度的最大限制为767 bytes。

总结如下:

  1. 对于myisam和innodb存储引擎,prefixes的长度限制分别为1000 bytes和767 bytes。注意prefix的单位是bytes,但是建表时我们指定的长度单位是字符。

  2. 以utf8字符集为例,一个字符占3个bytes。因此在utf8字符集下,对myisam和innodb存储引擎创建索引的单列长度不能超过333个字符和255个字符 。

测试过程如下:

关闭 innodb_large_prefix参数配置:

启用innodb_large_prefix参数配置:

从上面可以看出,mysql 在创建单列索引的时候对列的长度是有限制的 myisam和innodb存储引擎下长度限制分别为1000 bytes和767 bytes。(注意bytes和character的区别)

组合索引长度限制

对于innodb存储引擎,多列索引的长度限制如下:

每个列的长度不能大于767 bytes;所有组成索引列的长度和不能大于3072 bytes

参考如下例子(下面默认使用的使用innodb存储引擎,smallint 占2个bytes,timestamp占4个bytes,utf8字符集。utf8字符集下,一个character占三个byte)

1)组合索引列长度限制

innodb下多列索引,所有列长度和大于3072/3=1024 (255*4+5=1025),所以表创建失败

2)单列索引长度限制

索引列长度小于767(255*3=765),表创建成功

索引列长度大于767(256*3=768),所以创建表失败

对于myisam存储引擎,多列索引长度限制如下:

每个列的长度不能大于1000 bytes,所有组成索引列的长度和不能大于1000 bytes

解决方案

方案一:
using col_name(length) syntax to specify an index prefix length.

对列的前面某部分创建索引


之所以要限制索引键值的大小,是因为性能问题,而前缀索引能很好的解决这个问题。不需要修改任何系统变量。实例:mysql> show index from TEST;.................................. mysql> ALTER TABLE TEST DROP INDEX IDX_GEN_CODE;Query OK, 0 rows affected (0.00 sec)Records: 0 Duplicates: 0 Warnings: 0 mysql> CREATE IDX_GEN_CODE TEST ON TEST (CODE_NAME, CODE_VALUE1(12));Query OK, 0 rows affected (0.01 sec)Records: 0 Duplicates: 0 Warnings: 0 mysql> ALTER TABLE TEST MODIFY CODE_VALUE1 VARCHAR(350);Query OK, 1064 rows affected (0.08 sec)Records: 1064 Duplicates: 0 Warnings: 0

方案二:
启用innodb_large_prefix参数
  1. 启用innodb_large_prefix参数能够取消对于索引中每列长度的限制(但是无法取消对于索引总长度的限制)
  2. 启用innodb_large_prefix有如下前提条件:
    启用innodb_large_prefix必须同时指定innodb_file_format=barracuda,innodb_file_per_table=true,并且建表的时候指定表的row_format为dynamic或者compressed(mysql 5.6中row_format默认值为compact)
实例:

1.  查看innodb_large_prefix,innodb_file_format参数

mysql> show variables like 'innodb_large_prefix';+---------------------+-------+| Variable_name | Value |+---------------------+-------+| innodb_large_prefix | OFF |+---------------------+-------+1 row in set (0.01 sec)mysql> show variables like 'innodb_file_format';+--------------------+----------+| Variable_name | Value |+--------------------+----------+| innodb_file_format | Antelope |+--------------------+----------+1 row in set (0.00 sec)

2.  建索引测试(innodb_large_prefix,innodb_file_format都为默认值的情况下)


mysql> create table test3(id varchar(256),key (id)); ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.mysql> create table test3(id varchar(255),name varchar(255),name1 varchar(255),name2 varchar(255),name3 varchar(5),key (id,name,name1,name2,name3));ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

##索引列大于767 bytes时报错,组合索引列的总长度大于3072 bytes时报错

3.  修改innodb_large_prefix,innodb_file_format参数


mysql> set global innodb_large_prefix=1;Query OK, 0 rows affected (0.00 sec)mysql> set global innodb_file_format=BARRACUDA;Query OK, 0 rows affected (0.00 sec)

4.  对row_format为dynamic格式表创建索引测试


mysql> create table test3(id varchar(256),key (id)) row_format=dynamic;Query OK, 0 rows affected (0.14 sec)

如果表已创建, 那么查看row_format的格式是否为dynamic;


mysql> show table status from MyDB where name='TEST'\G;

修改行类型:


mysql> ALTER TABLE TEST ROW_FORMAT=DYNAMIC;Query OK, 0 rows affected (0.05 sec)Records: 0 Duplicates: 0 Warnings: 0mysql> create table test3(id varchar(255),name varchar(255),name1 varchar(255),name2 varchar(255),name3 varchar(5),key (id,name,name1,name2,name3)) row_format=dynamic; ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

##innodb_large_prefix=1并且innodb_file_format=BARRACUDA时,对于row_format为dynamic的表可以指定索引列长度大于767 bytes。但是索引列总长度的不能大于3072 bytes的限制仍然存在

 总  结 

ERROR 1071 (42000)解决方案:

启用系统变量innodb_large_prefix

注意:光有这个系统变量开启是不够的。必须满足下面几个条件:

  1. 系统变量innodb_large_prefix为ON

  2. 系统变量innodb_file_format为Barracuda

  3. ROW_FORMAT为DYNAMIC或COMPRESSED

注意事项:

  1. ##innodb_large_prefix=1并且innodb_file_format=BARRACUDA时,对于row_format为dynamic的表可以指定索引列长度大于767 bytes。

  2. 但是索引列总长度的不能大于3072 bytes的限制仍然存在,要解决这个问题只能使用前缀索引或者减小字段长度。

END

更多精彩干货分享

点击下方名片关注

IT那活儿

文章转载自IT那活儿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论