key_len并非越小或越大越好,而是越贴近实际查询所用索引列的理论字节长度,越说明索引被有效下推;它反映优化器是否走完联合索引前缀、是否存在隐式截断,并受数据类型、null标记、varchar存储结构三因素影响。

key_len 不是“越小越好”或“越大越好”,而是“越贴近实际查询条件所用索引列的理论长度,越说明索引被有效下推”。它直接暴露优化器有没有真正把联合索引的前缀走完、有没有因类型/NULL/字符集导致隐式截断或失效。
key_len 怎么算?别信“字段长度加起来”这种粗略说法
它不是表定义里 VARCHAR(255) 那个 255,也不是字符数,而是 MySQL 在索引 B+ 树节点里实际存和比对的字节数。关键影响项有三个:
- 数据类型基础长度:比如
INT固定 4 字节,TINYINT是 1 字节,DATETIME是 8 字节 - NULL 标记开销:只要列允许为
NULL,就额外 +1 字节(哪怕你查的是WHERE col = 1) - VARCHAR 的存储结构:在索引中,
VARCHAR(n)无论实际值多短,都按最大可能长度算,并 +2 字节长度前缀(utf8mb4 下就是n×4+2;CHAR(n)则是n×4)
例如:联合索引 (status TINYINT, name VARCHAR(50)),若两列都 NOT NULL,utf8mb4 环境下理论最大 key_len 是 1 + (50×4+2) = 203;但只要 name 允许 NULL,就变成 1+1 + 202 = 204 —— 多这 1 字节,B+ 树每页能存的键就少几个,深度可能多一层。
key_len 为 NULL 或远小于预期,说明索引根本没走全
常见于以下场景:
- 查询跳过联合索引中间列:
INDEX(a,b,c),但只查WHERE a=1 AND c=3→key_len只含a长度,b断层,c条件只能回表后过滤(Extra: Using where) - 隐式类型转换:
user_id INT索引,却写WHERE user_id = '123'→ 字符串转整数可能触发全索引扫描,key_len仍显示完整长度,但实际没生效(需看type和rows配合判断) - LIKE 左模糊:
WHERE name LIKE '%abc'→ 索引完全失效,key_len为NULL(type=ALL)
这时不能只盯着 key_len 数值,要结合 type(是否为 ref/range)、possible_keys 和 Extra 一起看。
key_len 接近理论最大值,也不代表查询快
它只说明“索引用得全”,不代表性能一定好:
- 如果联合索引太宽(比如包含多个
VARCHAR(255)),key_len很大 → 单页存的键变少 → B+ 树层级变深 → 更多次磁盘 I/O - 索引列选择性差(如
gender CHAR(1)),即使key_len满额,rows依然很高,大量回表 - 覆盖索引没做全:
SELECT *查所有列,但索引只含部分列 →key_len正常,但要回主键再查,Extra出现Using where; Using index(注意:后者表示用了索引,但前者说明还有过滤)
所以看到 key_len 高,先确认是不是真需要这么多列进索引;能 SELECT id,name 就别 SELECT *,能 WHERE status IN (1,2) 就别 WHERE status > 0 —— 后者虽然也走索引,但 key_len 相同,rows 却可能翻几倍。
真正容易被忽略的是:key_len 是静态计算值,它不随数据分布变化,但实际效率高度依赖数据倾斜程度和查询模式。同一索引,在不同 WHERE 条件下,key_len 可能一样,rows 却差十倍——这时候光看 key_len 会误判。











