MySQL 索引长度受字段类型、字符集及存储引擎限制,InnoDB 单列索引前缀上限为 767 字节(utf8mb3)或 3072 字节(utf8mb4+innodb_large_prefix=ON),超长报错;VARCHAR 等变长类型建索引需按实际字节占用决定是否指定前缀,如 utf8mb4 下 VARCHAR(200)需用 idx_name(name(191)),而 VARCHAR(100)可全字段索引;前缀长度应基于数据分布选择,并非固定 191;生成列 + 函数索引(如 email_domain)更优,要求 STORED 且函数确定性。

MySQL 索引长度由字段类型和字符集决定,不是随意设置的
MySQL 中 INDEX 的实际长度不等于你定义的列长度(比如 VARCHAR(255)),而是取决于该列的字符集、排序规则和存储引擎对索引前缀的限制。InnoDB 对单个索引列的前缀长度上限是 767 字节(utf8mb3)或 3072 字节(utf8mb4 + innodb_large_prefix=ON,且行格式为 DYNAMIC 或 COMPRESSED)。超长会直接报错:ERROR 1071 (42000): Specified key was too long。
varchar 字段建索引必须显式指定前缀长度?
不一定。只有当你对 VARCHAR、TEXT 或 BLOB 类型字段创建索引时,才需要考虑前缀长度——因为这类类型可能远超索引长度限制。而 INT、DATETIME、CHAR(10) 这类定长或短变长类型,通常可全字段索引,无需前缀。
-
VARCHAR(200)+utf8mb4→ 最大占 800 字节,超出 767,建普通索引必须加前缀,例如INDEX idx_name (name(191)) -
VARCHAR(100)+utf8mb4→ 最大 400 字节,可直接全字段索引:INDEX idx_name (name) - 使用
ALTER TABLE …… ADD INDEX时若未指定前缀且触发长度超限,MySQL 不会自动截断,而是报错退出
前缀长度选 191 是万能解?
191 来自 767 ÷ 4(utf8mb4 最多每字符 4 字节),但它只是安全上限,并非最优选择。实际应基于数据分布来定:
- 先查字段值的实际前 N 位重复率:
SELECT COUNT(DISTINCT LEFT(email, 50)) / COUNT(*) FROM users;若结果接近 1,说明前 50 字符已足够区分大部分值
- 过长前缀浪费 B+ 树空间,降低缓存效率;过短则导致大量哈希 / 比较冲突,反而拖慢查询
- 对 邮箱、URL 等有结构的字段,常取域名或协议后部分(如
LEFT(url, 255)不如SUBSTRING_INDEX(url, '/', 3)配合生成列索引)
用生成列 + 函数索引替代长前缀(MySQL 5.7+)
比硬凑前缀更可控的方式是把提取逻辑下沉到列本身,再建普通索引:
ALTER TABLE users ADD COLUMN email_domain VARCHAR(255) GENERATED ALWAYS AS (SUBSTRING_INDEX(email, '@', -1)) STORED, ADD INDEX idx_email_domain (email_domain);
这样既避免前缀不确定性,又让优化器能准确估算选择性。注意:STORED 列才支持索引,VIRTUAL 列不行;且函数必须是确定性的(SUBSTRING_INDEX 符合)。
真正难的不是算字节数,而是判断「哪些前缀真能过滤掉无效行」——这得看业务数据,不是套公式能解决的。