key_len仅表示MySQL实际比较时使用的字节数,需结合索引定义、字段类型、NULL属性和字符集反推使用了哪些前缀字段;常见错误是混淆定义长度与实际使用长度、忽略NULL标志位和字符集影响。

怎么看 key_len 值对应用了几个索引字段
直接看 key_len 不能自动告诉你“用了前2个还是前3个字段”,它只反映 MySQL 实际比较时用到的字节数。得结合索引定义、字段类型、是否允许 NULL、字符集这四点反推。
常见错误现象:EXPLAIN 显示 key_len=78,但你建的是 (a INT, b VARCHAR(20), c DATETIME) 联合索引,却不确定 b 有没有参与查找。
- 先查索引字段顺序和类型:
SHOW INDEX FROM table_name - 按顺序累加每个字段的「可能最大字节长度」:INT 固定 4 字节;
VARCHAR(20)在 utf8mb4 下是20 × 4 = 80字节,但实际只算「该字段在查询中用到的长度」 - 如果字段定义为
NOT NULL,不额外加 1 字节;否则每字段多占 1 字节用于标记 NULL - 字符串字段若用前缀索引(如
INDEX(a, b(10))),只按前缀长度算(10 × 4 = 40)
key_len 计算公式里哪些地方容易算错
最常踩的坑是把「定义长度」当「实际使用长度」,或忽略字符集和 NULL 标志位。
-
TINYINT是 1 字节,INT是 4 字节,BIGINT是 8 字节 —— 和是否带符号无关 -
VARCHAR按实际字符数 × 字符集单字符最大字节数:utf8mb4 下每个字符最多 4 字节,不是按定义长度全算满 - 如果字段允许 NULL,且该字段在联合索引中排在“被用到的最后一个字段”之前或等于它,就+1;但如果该字段根本没出现在
WHERE条件里,就不加 - 时间类型如
DATETIME在 5.6.4+ 是 5 字节(微秒精度占 1 字节),老版本是 8 字节;TIMESTAMP固定 4 字节
示例:索引为 (a INT, b VARCHAR(50), c TINYINT),查询 WHERE a = 1 AND b = 'x',假设表用 utf8mb4、所有字段 NOT NULL:key_len = 4 + (1 × 4) = 8(b 只匹配 1 个字符,不是 50)
为什么 key_len 突然变小了,但条件没改
说明 MySQL 优化器决定跳过后面字段,常见于范围查询中断最左前缀原则。
-
WHERE a = 1 AND b > 'x' AND c = 3→ 只用到 a 和 b,c不进key_len计算(即使c有等值条件) -
WHERE a = 1 ORDER BY b DESC, c ASC→ 如果b和c排序方向不一致,可能只用到a,b,c不参与索引扫描 - 隐式类型转换也会导致截断,比如
a是VARCHAR但传入数字:MySQL 转成字符串再比对,可能无法用完整前缀 - 函数操作字段(
WHERE UPPER(b) = 'X')直接让整个字段失效,key_len只计前面可用字段
不同 MySQL 版本对 key_len 的解释有差异吗
有,主要集中在时间类型和 JSON 字段,但日常用的整型、字符串影响极小。
- 5.6.4 之前:
DATETIME固定 8 字节;之后默认支持微秒,变成 5 字节(无微秒)或 8 字节(含微秒) - 8.0.13+ 支持
JSON字段加索引,但key_len对 JSON 路径表达式不友好,基本看不出用了哪一段路径 - 分区表下
key_len不体现分区键额外开销,别拿它估算整体 IO 成本 - 唯一索引和普通索引在
key_len计算上完全一致,不因唯一性多算字节
真正容易被忽略的是:同一个查询在不同版本 EXPLAIN 出的 key_len 可能不同,不是你算错了,而是优化器选了不同执行路径 —— 此时要配合 optimizer_trace 看决策依据,不能只盯一个数字。










