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符合)。
真正难的不是算字节数,而是判断「哪些前缀真能过滤掉无效行」——这得看业务数据,不是套公式能解决的。
