本质是索引键字节数超限而非字段长度本身:MySQL按字符集最大字节宽(如utf8mb4为4字节/字符)预估索引长度,VARCHAR(255)理论达1020字节,超过InnoDB旧版硬编码限制767字节;该限制源于16KB页需容纳至少两条索引记录的B+树物理约束,且启用3072字节需innodb_large_prefix=ON、ROW_FORMAT=DYNAMIC/COMPRESSED、innodb_file_per_table=ON三者同时满足。

索引键字节数超限,不是字段本身太长
报错 Specified key was too long; max key length is 767 bytes 的本质,是 MySQL 在构建索引时计算出的「索引键总字节数」超过了当前 InnoDB 实际允许的上限,而不是字段定义长度(如 VARCHAR(255))本身不合法。例如:VARCHAR(255) + utf8mb4 → 最坏情况占 255 × 4 = 1020 字节,已超 767,哪怕字段实际只存 1 个字符也会失败。
关键点在于:MySQL 按字符集最大字节宽度预估索引长度,且该预估不可绕过——即使你确定字段永远只存 ASCII,InnoDB 仍按 utf8mb4 的 4 字节/字符来算。
767 是旧版 InnoDB 的硬编码页结构限制
这个数字不是配置项,也不是“建议值”,而是 MySQL 5.6 及更早版本中 InnoDB 的物理约束:
- InnoDB 默认页大小为 16KB,非叶子节点需至少容纳两条索引记录,否则 B+ 树退化
- 聚簇索引和二级索引的键值必须完整存入页内(不能像大字段那样溢出),扣除事务 ID、指针等元数据后,留给键值的空间约 767 字节
- 对
utf8(3 字节/字符),255 × 3 = 765,所以早期默认兼容VARCHAR(255);换成utf8mb4后,191 × 4 = 764才安全
为什么改了 innodb_large_prefix=ON 还是报错?
启用大前缀支持需要**三个条件同时满足**,缺一不可:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
-
innodb_large_prefix = ON(MySQL 5.7.7+ 默认开启,5.6 需手动设) -
ROW_FORMAT = DYNAMIC或COMPRESSED(不能是默认的COMPACT或REDUNDANT) -
innodb_file_per_table = ON(RDS 等托管服务通常默认开)
常见陷阱:ALTER TABLE ... ROW_FORMAT=DYNAMIC 必须配合 ALGORITHM=INPLACE(MySQL 5.6+)或重建表(COPY),否则不生效;且已有数据不会自动迁移,需 OPTIMIZE TABLE 或重建。
联合索引更容易踩坑,计算方式不同
联合索引不是“每列 767”,而是所有列的**理论最大字节长度之和**不能超限:
- MySQL 5.6:单列 ≤ 767 字节,联合索引总和也 ≤ 767 字节(不是 3072)
- MySQL 5.7.7+:单列 ≤ 3072 字节,但联合索引总和仍 ≤ 3072 字节(注意不是每列 3072)
- 例如:
KEY idx_composite (email(191), name(191))在utf8mb4下就是191×4 + 191×4 = 1528字节 → 即使在 5.7+ 也远未超 3072,但若再加一列就可能崩
最易被忽略的是:TEXT/BLOB 字段建索引必须显式指定前缀长度(如 content(255)),否则直接按全长计算,极易超限。

















