mysql索引长度限制是innodb为保障b+树性能设定的硬约束:5.7.6及之前默认单列索引上限767字节(如utf8mb4下最多191字符),5.7.7+需满足版本、innodb_large_prefix=on、row_format=dynamic/compressed三条件才启用3072字节上限;联合索引总长为各列前缀字节数之和,任一列超限即创建失败。

MySQL索引长度限制不是bug,而是InnoDB为保证B+树索引性能做的硬性约束。767字节是旧默认值,3072字节是启用特定配置后的上限——但这两个数字不会同时生效,具体取决于你的MySQL版本、行格式和参数设置。
为什么InnoDB单列索引最大是767字节
这个数字源于InnoDB页大小(16KB)和B+树节点结构的设计权衡:每个索引页需存储键值、指针、事务ID、回滚指针等元数据,留给键值的空间必须可控。767字节 ≈ 255字符 × 3字节(旧utf8最大单字符长度),是MySQL 5.6及之前版本的默认硬限制。
关键点:
-
innodb_large_prefix在5.6默认为OFF,此时无论什么行格式,单列索引都卡死在767字节 - 即使字段定义是
varchar(255),用utf8mb4时实际可能占255×4=1020字节 → 超限 - 错误信息
ERROR 1071: Specified key was too long; max key length is 767 bytes就是最直接的信号 - 该限制只作用于索引键本身,不影响字段存储长度(
alter table ... modify column扩字段不会触发此错)
什么时候能用到3072字节上限
3072不是“升级就自动生效”的新上限,它需要三个条件全部满足:
- MySQL ≥ 5.7.7(或8.0,默认已支持)
-
innodb_large_prefix=ON(5.7.7+默认开启;5.6需手动改配置并重启) - 表的
ROW_FORMAT必须是DYNAMIC或COMPRESSED(不能是COMPACT或REDUNDANT)
缺一不可。常见坑:
- 从老版本迁移的表,
SHOW TABLE STATUS查出来仍是Row_format: COMPACT,即使参数开了也无效 -
ALTER TABLE ... ROW_FORMAT=DYNAMIC必须配合ALGORITHM=INPLACE才不锁表,否则大表会卡住 - 云数据库如阿里云RDS,部分低配实例不支持修改
innodb_large_prefix,只能靠改行格式+前缀索引
utf8mb4下怎么算安全的前缀长度
别硬背191这个数——它只在767字节限制下成立。真实计算公式是:floor(最大允许字节数 / 字符集单字符最大字节数)。
例如:
- 767 ÷ 4 = 191 →
varchar(255)字段建索引必须写email(191) - 3072 ÷ 4 = 768 → 同样字段可写
email(768),但注意varchar定义本身不能超长(如定义为varchar(500),前缀最多只能取500) - 如果字段是
text,必须显式指定前缀,如add index idx_content(content(300))
验证方法:
SELECT COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'your_table' AND COLUMN_NAME = 'your_column';
再结合SHOW VARIABLES LIKE 'character_set_database';确认实际生效字符集。
联合索引总长度怎么算,为什么更容易超
联合索引不是简单相加,而是各列前缀长度×各自字符集字节数之和。比如:
KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`)
若三列都是utf8mb4且未指定前缀,按定义长度算:50+255+255 = 560字符 → 560×4 = 2240字节 → 远超767。
所以联合索引更需精简:
- 优先保留高区分度字段(如
registry_key),砍掉低区分度的(如registry_value) - 对每列单独加前缀,如
(registry_group(20), registry_key(191)) - 避免在
TEXT/VARCHAR(1000)字段上参与联合索引,除非业务明确需要且已评估前缀有效性
真正容易被忽略的是:联合索引中只要有一列超限,整个索引创建就失败——哪怕其他列很短。











