前缀索引无法支持覆盖查询,因其只存储字段前N个字节,不包含完整值;即使EXPLAIN显示Using index,若key_len小于字段实际字节数,仍需回表;联合索引中含前缀列也无法覆盖涉及该列完整值的查询。

前缀索引不存储完整字段值,所以无法覆盖查询
前缀索引(如 INDEX(name(10)))只保存字段前 N 个字节的内容,MySQL 无法用它返回超出长度的原始值。覆盖查询要求所有 SELECT 列都从索引中直接获取,不回表;但前缀索引里没有完整 name,自然不能满足。
EXPLAIN 中 key_len 小于字段实际字节数就是线索
执行 EXPLAIN SELECT name FROM users WHERE name LIKE 'abc%' 后,如果 key_len 显示为 30(假设 name 是 VARCHAR(255)、utf8mb4 编码),而你建的是 INDEX(name(10)),那 key_len 通常只有 40(10 字符 × 4 字节),说明只用了前缀部分 —— 这时即使 Extra 显示 Using index,也仅表示“用了索引”,不代表“覆盖了查询”。真正覆盖必须所有 SELECT 列都在索引结构内完整存在。
联合索引加前缀列仍可能无法覆盖
比如 INDEX(status, name(10), created_at),查询 SELECT status, name FROM ... 依然不覆盖,因为 name 在索引里是截断的。哪怕 created_at 被选中,只要 name 需要原值,就得回表读完整行。
- 能覆盖的写法:
INDEX(status, name, created_at)(无前缀) - 或只查前缀长度内的内容:
SELECT LEFT(name, 10) FROM ...(但业务意义常受限) -
SELECT status单独查是可以覆盖的,因为status是完整存的
前缀索引的真正价值不在覆盖,而在节省空间和加速前缀匹配
它适合长文本字段高频按前缀过滤(如 URL、邮箱域名),但别指望靠它减少回表。如果你发现慢查询卡在回表,优先检查是否误用了前缀索引代替完整列索引,而不是调大前缀长度。
容易被忽略的一点:前缀长度选得太小(比如对中文字段设 name(2)),连基本区分度都不够,索引就变成摆设 —— 此时 EXPLAIN 可能显示 key 有值,但 rows 高得离谱,本质仍是低效扫描。


















