key_len是mysql实际使用的索引字节数,非定义长度;它反映复合索引用了几列、是否因null/字符集/前缀索引产生额外开销,值越接近索引总长说明利用越充分。

key_len 不是索引定义长度,而是 MySQL **实际用到的索引字节数**。它直接反映查询是否走全索引、复合索引用了几列、有没有因 NULL 或字符集膨胀导致额外开销——看懂它,才能判断索引是不是真被高效利用了。
定长类型(int / datetime / char)的 key_len 计算
这类字段长度固定,计算最直观,但容易忽略 NULL 标记带来的 +1 开销。
-
NOT NULL字段:只算类型本身字节数,比如int是 4 字节,datetime是 8 字节,char(10)在utf8mb4下就是 10 × 4 = 40 字节 -
允许为 NULL的字段:在类型字节数基础上 +1,用于标记该值是否为NULL - 注意
char和varchar的根本区别:char(10)无论存几个字符都占满 10 个字符位,而varchar是变长的,不在此类
示例:create_time datetime null → key_len = 8 + 1 = 9;status tinyint not null → key_len = 1。
变长类型(varchar / text 前缀索引)的 key_len 计算
varchar 是最常见的“陷阱区”:它的 key_len = 字符数 × 字符最大字节 + 长度前缀(2 字节)+ NULL 标记(1 字节,如允许 NULL)。
- 长度前缀固定 2 字节:告诉 MySQL 这个值实际存了几字节(因为
varchar可变) - 字符集决定单字符字节数:
utf8mb4下 1 字符 = 4 字节,gbk下 = 2 字节,latin1= 1 字节 - 哪怕你只查
WHERE name = 'a',MySQL 仍按定义长度(如varchar(50))乘以字符集字节数来算理论最大占用 - 如果字段定义为
NOT NULL,就不用加最后那个 1 字节
示例:name varchar(50) not null,表用 utf8mb4 → key_len = 50 × 4 + 2 = 202;若允许 NULL,则为 203。
复合索引中 key_len 的累加逻辑
MySQL 对复合索引的 key_len 是「从左到右逐列累加」,每列独立套用上述规则,不是整体估算。
- 比如联合索引
(a, b, c),查询WHERE a = ? AND b = ?,则key_len = len(a) + len(b),c不参与计算 - 一旦中间某列没出现在
WHERE条件里(如只查a和c),后续列全部失效,key_len就只含a的部分 - 注意隐式类型转换:比如
a int索引列,却用字符串'123'查询,会导致该列索引失效,key_len变为NULL
示例:索引 idx_user(name, age, city),其中 name varchar(20) not null(utf8mb4)、age tinyint not null、city varchar(10) null → 全部命中时 key_len = (20×4+2) + 1 + (10×4+2+1) = 82 + 1 + 43 = 126。
key_len 为 NULL 或远小于预期时意味着什么
key_len 显示 NULL 或明显偏小(比如 varchar(100) 字段只显示 2),基本可断定索引未被有效使用。
-
key_len IS NULL:说明优化器没选这个索引,可能因为统计信息过期、条件带函数(如WHERE UPPER(name) = 'A')、或存在更优索引 - 值异常小:常见于对
varchar字段做了前缀索引(如INDEX(name(10))),此时按前缀长度算,而非定义长度 - 值比预估少一列:说明复合索引断裂,检查条件是否满足最左前缀原则,以及是否存在隐式转换(如数字列传入字符串)
真正关键的不是记住所有公式,而是每次看到 key_len,立刻反推:「哪几列被用了?有没有因 NULL/字符集/前缀/类型转换多占字节?为什么没用满?」——这些细节藏在字节差里,而不是执行计划的其他列中。











