在为数据库某字段(varchar(255))创建索引的时候出现如下问题:
Specified key was too long; max key length is 767 bytes
出现这个问题是因为创建索引时指定的字段超过了存储引擎默认的长度。
数据库存储引擎不同导致的索引长度限制
MySQL 的每个单表中所创建的索引长度是有限制的,且对不同存储引擎下的表有不同的限制。
- MYISAM 表,单列索引,最大长度不能超过 1000 bytes,否则会报警,但是创建成功,最终创建的是前缀索引(取前333个字符)。
- MYISAM 表,组合索引,索引长度和不能超过 1000 bytes,否则会报错,创建失败;
- INNODB 表,单列索引,超过 767 bytes的,给出warning,最终索引创建成功,取前缀索引(取前 255 字符)。
- INNODB 表,组合索引,各列长度不超过 767 bytes ,如果有超过 767 bytes 的,则给出报警,索引最后创建成功,
但是对于超过 767 字节的列取前缀索引,与索引列顺序无关,总和不得超过 3072 ,否则失败,无法创建。
MySQL 版本不同导致的索引长度限制
在 MySQL5.5 版本,引入了 innodb_large_prefix,用来禁用大型前缀索引,以便与不支持大索引键前缀的早期版本的 InnoDB 兼容
开启 innodb_large_prefix 可以使单索引的长度限制达到 3072 字节(但是联合索引总长度限制还是 3072 字节),禁用时单索引的长度限制为 767 字节
在 MySQL5.5版本与MySQL5.6 版本,innodb_large_prefix 是默认关闭的,在 MySQL5.7 及以上版本则默认开启
在 MySQL8.0 版本中,innodb_large_prefix 已被移除,从版本 8.0 开始,索引长度限制由表字段(row format)决定,
若为 DYNAMIC 或 COMPRESSED 时,限制值为 3072;为 REDUNDANT 或 COMPACT 时,限制值为 767。
为什么 3072:
InnoDB 一个 page 的默认大小是 16 k。由于是 Btree 组织,要求叶子节点上一个 page 至少包含两条记录(否则就退化链表了)。
所以一个记录最多不能超过 8 k。又由于 InnoDB 的聚簇索引结构,一个二级索引要包含主键索引,因此每个单个索引不能超过 4 k(极端情况,pk 和某个二级索引都达到这个限制)。
由于需要预留和辅助空间,扣掉后不能超过 3500,取个“整数”就是(1024 * 3 = 3072)。
数据库表字符集不同导致的索引长度限制
utf8 与 utf8mb4 的区别:
由于历史原因,MySQL 刚开始设计的时候,”天真的”认为使用 3 个字节就足够存储字符串了,因此将 UTF-8 进行阉割;然而遇到复杂的汉字或者 emoji 表情等 4 字节的宽字符的时候,存储就会出现异常,
因此在版本 5.7.3 开始引入 utf8mb4,其表示为 most bytes 4,即最多占用 4 个字节。
utf8占用3个字节,utf8mb4占用4个字节,根据使用的存储引擎的不同,我们可以列出下面的表格。可以看到utf-8字符集的表使用INNODB存储引擎其索引字段的长度最大为256。
utf-8 | utf-8mb4 | |
---|---|---|
MYISAM | 1000/3=333 | 1000/4=250 |
INNODB | 768/3=256 | 768/4=192 |
回到我们出现的问题,我们添加索引的字段长度为255,如果是UTF-8字符集理论上是不超过索引长度限制的。但经过查看数据库表结构发现,此数据库表建表时使用了utf8mb4字符集。因此此时的长度限制应为192,。
原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/277344.html