首页/数据/SQL 速查/key_len 判断复合索引用了几列
性能与执行计划72 条

key_len 判断复合索引用了几列

key_len 是索引查找实际用到的字节数:对照列定义宽度可以推断复合索引命中前几列,验证前缀使用情况。

SQL 速查数据更新于 2026-10-13

key_len 是什么

key_len 是 EXPLAIN 输出里「本次索引查找实际使用的字节数」,对照列定义的宽度就能反推出复合索引用到了前几列。它验证的是最左前缀到底走了多深:key_len 只覆盖第 1 列,说明后面的列没有参与索引查找,最多只能在回表后作为过滤条件。计算宽度时要考虑类型长度、字符集、是否允许 NULL,以及 VARCHAR 的长度前缀。

基本信息

适用平台MySQL 8.0(PostgreSQL 没有 key_len,改看索引条件与 BUFFERS 输出)
常见宽度INT 4 字节、BIGINT 8、DATE 3、DATETIME 5(不含小数秒)
字符列utf8mb4 按 4 字节/字符计入,不是 3 字节
可空列额外增加 1 字节 NULL 标志位
VARCHAR额外增加 1-2 字节长度前缀,按声明的字节上限决定
用途反推复合索引实际命中了前几列

典型使用场景

  • 验证复合索引是否真的用上了第 2、3 列
  • 排查「索引建了却只用了一列」的原因
  • 确认 ORDER BY 的列是否参与了索引查找
  • 对比改写前后索引前缀使用情况的变化
  • 定位隐式类型转换导致的 key_len 异常

反推命中的列数

反推命中的列数
ALTER TABLE orders ADD INDEX idx_user_status_created (user_id, status, created_at);

EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';
# key: idx_user_status_created
# key_len 对应 user_id 与 status 两列宽度:第 3 列 created_at 未参与索引查找

EXPLAIN SELECT * FROM orders WHERE user_id = 100;
# key_len 只覆盖 user_id:仅用到第 1 列

EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND created_at > '2026-01-01';
# 跳过 status 直接按 created_at 过滤时,created_at 无法用于索引查找

宽度速查

列类型字节宽度备注
TINYINT / SMALLINT1 / 2整数类型宽度固定
INT / BIGINT4 / 8最常用的两类
DATE / DATETIME3 / 5DATETIME 不含小数秒,带精度会额外增加
VARCHAR(n) utf8mb44n + 1 或 2加长度前缀 1-2 字节
可空列额外 +1NULL 标志位
DECIMAL(10,2)5每 9 位十进制占 4 字节,另有符号与小数开销
key_len 只说明索引查找用了几列,过滤效果还要看 Extra 与 rows 估算:只用到前导列不等于查询一定慢,如果该列过滤后行数已经很少,剩余条件作为回表过滤也完全可行。
按 UTF-8 每字符 3 字节估算会得出错误结论:MySQL 的 utf8mb4 按 4 字节/字符计入 key_len,算错就会误判索引的使用情况。
记录基线很有用:把关键查询的 key、key_len、rows 抄进变更记录,下次改索引或升级版本后对比,能立刻看出是索引使用范围变了还是数据分布变了。

易错点

  • 用 3 字节/字符估算 utf8mb4 的 key_len,反推出的列数与实际不符
  • 看到 key_len 小于整条索引就断定索引失效,实际只是命中前导列的常态
  • 忽略可空列与 VARCHAR 长度前缀带来的额外字节,对不上推算结果
  • 只盯 key_len 不看 rows 与 filtered,忽略「用上索引但过滤效果差」的情况

数据来源与参考

  • MySQL 8.0 Reference Manual §8.8.2 EXPLAIN Output Format(key_len)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌