该错误是InnoDB在MySQL 5.6及更早版本中的硬编码限制,源于B+树页结构设计:默认16KB页需容纳至少两条索引记录,扣除元数据后索引键值上限约767字节;utf8mb4下对应191字符(191×4=764字节),超限即报错,且需同时满足innodb_large_prefix=ON、ROW_FORMAT=DYNAMIC/COMPRESSED及innodb_file_format=BARRACUDA才能突破至3072字节。

Specified key was too long; max key length is 767 bytes 这个错误不是“建议”,而是 InnoDB 在 MySQL 5.6 及更早版本中硬编码的限制——它源于 B+ 树页结构和存储引擎设计约束,不是可选的工程建议。
767 字节来自 InnoDB 的页内索引记录布局
InnoDB 默认页大小为 16KB,而每个索引页(非叶子节点)必须能容纳至少两条索引记录,否则 B+ 树会退化为链表。主键作为聚簇索引,其值要完整存入页内(不能像大字段那样溢出到单独的溢出页),因此单条索引记录长度被严格限制在约 8KB 以内;再扣除指针、事务 ID、回滚指针等元数据后,留给索引键值的空间就只剩约 4KB。767 是早期针对 utf8(最多 3 字节/字符)推导出的安全上限:256 × 3 − 1 = 767。
utf8mb4 下 767 实际对应 191 字符
当使用 utf8mb4 字符集(如支持 emoji)时,每个字符最多占 4 字节,所以:
-
varchar(191)→ 最坏情况 191 × 4 = 764 字节,刚好低于 767 -
varchar(192)→ 192 × 4 = 768 字节 → 触发Specified key was too long
这个数字不是凑巧,是字节上限倒推出来的整数边界。很多 ORM 自动生成 varchar(255) 并直接建索引,一用 utf8mb4 就崩,根源就在这儿。
为什么不能简单调大?涉及底层文件格式兼容性
即使你执行 SET GLOBAL innodb_large_prefix = ON,也必须同时满足三个条件才能让单列索引突破 767:
innodb_file_format = BARRACUDAinnodb_file_per_table = ON- 表的
ROW_FORMAT必须是DYNAMIC或COMPRESSED(不能是默认的COMPACT)
缺一不可。否则 ALTER TABLE ... ADD INDEX 仍会报错 Index column size too large. The maximum column size is 767 bytes. —— 这说明限制不是靠参数“开关”就能绕过,而是和物理存储格式强绑定。
MySQL 5.7+ 默认放开,但 3072 不是万能解药
MySQL 5.7.7+ 默认启用 innodb_large_prefix,单列索引上限升为 3072 字节,但联合索引总长仍受限于 3072 字节(不是每列 3072)。例如:
KEY idx_composite (a, b, c)
若 a、b、c 都是 varchar(255) + utf8mb4,则理论长度为 255×4×3 = 3060 字节,看似刚好;但实际还要计入 NULL 标志位、长度前缀等额外开销,稍不留神就超限。这种边界问题最容易在迁移或升级后暴露。
varchar(255) CHARACTER SET utf8mb4,InnoDB 就按最坏情况(4 字节/字符)计算——它只看元数据,不看数据分布。


















