索引列长度影响innodb页内存储密度,越长则单页容纳索引项越少,导致查询需读取更多页、增加i/o和cpu开销;767字节为默认单列索引上限,超长需用前缀索引,但须验证选择性,且联合索引中应将短而高选择性列置左。

索引列长度影响InnoDB页内存储密度
MySQL(尤其是InnoDB)的索引数据按页(page)组织,默认页大小为16KB。索引越长,单个页能容纳的索引项就越少,意味着查询时需要读取更多页才能定位目标行——这直接增加I/O次数和CPU比较开销。innodb_page_size不可动态修改,所以压缩索引长度本质是在有限空间里“塞进更多线索”。
- 例如:
VARCHAR(255)字段若全量建索引,实际可能只存10个字符,但索引仍按255字节预留空间(含长度前缀),浪费严重 -
TINYINT比BIGINT主键节省7字节/记录,而二级索引节点中会重复存储主键值,放大节省效果 - InnoDB对单列索引长度上限为767字节(
innodb_large_prefix=OFF时),超长字段必须用前缀索引,否则ERROR 1071 (42000): Specified key was too long
前缀索引不是万能的,需验证选择性
用ALTER TABLE t ADD INDEX idx_name (name(10))这类前缀索引能绕过长度限制,但前提是前10个字符足够区分绝大多数值。否则查WHERE name = 'Alice'时,引擎可能因前缀重复率高而放弃走索引。
- 先统计前缀区分度:
SELECT COUNT(DISTINCT LEFT(name, 10)) / COUNT(*) FROM t;结果接近1才安全 - 避免对
ENUM或BOOLEAN类低基数列建前缀索引——LEFT(status, 1)和全量索引效果一样差 - UTF8MB4下中文字符占3~4字节,
name(10)实际可能只覆盖2~3个汉字,需按字节数而非字符数估算
联合索引中列顺序决定前缀有效性
复合索引(a, b, c)天然支持(a)、(a,b)、(a,b,c)三种最左前缀,但b或c单独出现时无法利用该索引。如果a本身很长(如VARCHAR(100)),整个联合索引的物理尺寸会被拖累,即使b和c很短。
- 把高选择性且短的列放在联合索引最左侧,比如
(user_id, created_at)优于(email, user_id)(email通常更长) - 若必须包含长字段,优先让它靠后,避免阻断前面短列的前缀匹配能力
-
EXPLAIN结果中key_len值能反映实际用了多少字节索引,对比key_len和预期前缀长度可验证是否生效
默认值NULL会让索引失效,比长度问题更隐蔽
哪怕你把VARCHAR(200)缩成VARCHAR(20)并加前缀索引,只要该列允许NULL,InnoDB就可能拒绝使用它——因为B+树索引不存储NULL值,导致优化器判定索引覆盖不全而退化为全表扫描。
- 建表时显式声明
NOT NULL DEFAULT ''或NOT NULL DEFAULT 0,比事后补约束更可靠 - 已有表改结构:
ALTER TABLE t MODIFY COLUMN desc TEXT NOT NULL DEFAULT '';,注意TEXT类型不能有默认值,需换为VARCHAR -
WHERE col IS NULL永远无法走普通B-Tree索引,这是设计使然,缩短长度也无济于事











