key_len 计算和作用

key_len 计算和作用

key_len 是什么

EXPLAIN 输出中的 key_len 列表示 MySQL 在索引中实际使用的字节数

EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 25;
+-------+----------+---------+-------+
| key   | key_len  | ref     | rows  |
+-------+----------+---------+-------+
| idx_name_age | 157 | const,const | 1 |
+-------+----------+---------+-------+

key_len 的作用:判断复合索引中真正使用了多少列

为什么需要 key_len

-- 复合索引:idx_name_age_status(name, age, status)

-- 查询 1
EXPLAIN SELECT * FROM user WHERE name = '张三';
-- key_len: 152  → 只用到了 name 列

-- 查询 2
EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 25;
-- key_len: 157  → 用到了 name + age

-- 查询 3
EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 25 AND status = 1;
-- key_len: 158  → 用到了 name + age + status

原理:通过 key_len 推算 MySQL 使用了复合索引中的多少列,是了解最左前缀具体应用情况的准确手段。

key_len 的计算规则

key_len 的计算基于索引列的数据类型、是否可为空、字符集等:

key_len = 字段长度 + 变长标识 + NULL 标识

1. 固定长度字段:直接使用字段长度
2. 可变长度字段:字段长度 + 2 字节(变长标识)
3. 可为 NULL:额外 + 1 字节(NULL 位图)
4. 字符集影响:
   - utf8mb4:每个字符最多 4 字节
   - utf8:每个字符最多 3 字节
   - latin1:每个字符 1 字节

常见类型的 key_len 计算

INT 类型

-- TINYINT   = 1 字节
-- SMALLINT  = 2 字节
-- MEDIUMINT = 3 字节
-- INT       = 4 字节
-- BIGINT    = 8 字节

CREATE TABLE t (
    id BIGINT NOT NULL,         8 字节
    age INT NOT NULL,           4 字节
    score INT DEFAULT NULL      4 + 1(NULL) = 5 字节
);

VARCHAR 类型

-- VARCHAR(n) 在 utf8mb4 下
-- 最大字节 = n × 4
-- 加上 2 字节变长标识
-- 如可为 NULL 再加 1 字节

CREATE TABLE t (
    name VARCHAR(50) NOT NULL,
    -- utf8mb4: 50 × 4 + 2(变长) = 202 字节

    email VARCHAR(100) DEFAULT NULL,
    -- utf8mb4: 100 × 4 + 2(变长) + 1(NULL) = 403 字节
);

日期/时间类型

-- DATE      = 3 字节
-- TIME      = 3 字节
-- DATETIME  = 5 字节(不含毫秒)
-- TIMESTAMP = 4 字节
-- YEAR      = 1 字节

实战计算示例

-- 表结构
CREATE TABLE user (
    id BIGINT PRIMARY KEY,           -- BIGINT NOT NULL = 8
    name VARCHAR(50) NOT NULL,       -- utf8mb4: 50×4+2 = 202
    age INT NOT NULL,                -- 4
    status TINYINT DEFAULT NULL,     -- 1 + 1(NULL) = 2
    created_at DATETIME DEFAULT NULL -- 5 + 1(NULL) = 6
);

-- 复合索引
CREATE INDEX idx_name_age_status ON user(name, age, status);

-- 验证
EXPLAIN SELECT * FROM user
WHERE name = '张三' AND age = 25;
+------+---------------------+----------+...
| type | key                 | key_len  |
+------+---------------------+----------+
| ref  | idx_name_age_status | 206      |
+------+---------------------+----------+

key_len = 206 的计算:

name VARCHAR(50) NOT NULL:    50×4 + 2 = 202
age INT NOT NULL:             4
合计:                         206

status 没有用到 → 不在 key_len 中
如果用到 status: 206 + 2 = 208

进一步验证

-- 只用 name
EXPLAIN SELECT * FROM user WHERE name = '张三';
-- key_len: 202

-- 用 name + age
EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 25;
-- key_len: 206

-- 用 name + age + status
EXPLAIN SELECT * FROM user WHERE name = '张三' AND age = 25 AND status = 1;
-- key_len: 208

-- 范围截断:name + age(范围)
EXPLAIN SELECT * FROM user WHERE name = '张三' AND age > 20;
-- key_len: 206(age 用到了范围条件,status 被截断)

-- 只用 name, c 走 ICP
EXPLAIN SELECT * FROM user WHERE name = '张三' AND status = 1;
-- key_len: 202(只有 name,status 在 ICP 或回表后过滤)

key_len 不是精确”用了哪些列”?

key_len 精确反映了有多少字节被用于索引查找,但不是恰好这些列。

-- 特殊情况:范围条件也会计入 key_len
-- 索引 idx(a, b, c)

-- WHERE a = 1 AND b > 10 AND c = 3
-- key_len 包含 a + b(b 是范围条件,c 被截断)
-- 即 key_len = len(a) + len(b)

key_len 在优化中的应用

-- 1. 验证最左前缀是否满足
EXPLAIN SELECT * FROM t WHERE b = 2;
-- key: idx_a_b, key_len: 0 或很小 → b 其实没用到
-- 实际 key_len 应该反映出只有 a 或没有用到

-- 2. 确认范围截断
EXPLAIN SELECT * FROM t WHERE a = 1 AND b > 5 AND c = 3;
-- key_len = len(a) + len(b)  → c 被范围截断

-- 3. 检查 ICP 效果
EXPLAIN SELECT * FROM t WHERE a = 1 AND c = 3;
-- key_len = len(a)  → c 通过 ICP(Using index condition)过滤

面试要点

  • key_len 表示索引使用的字节数,不是列数
  • 核心用途:判断复合索引在 WHERE 条件中真正用了多少列
  • 计算要素:数据类型字节 + 字符集系数 + 变长标识(2字节) + NULL标识(1字节)
  • 常见计算:VARCHAR(50) utf8mb4 NOT NULL = 202,INT NOT NULL = 4
  • 范围截断:范围条件之后的所有列 key_len 不包含
  • ICP 不增加 key_len:索引下推的列不增加 key_len 长度

一句话总结:key_len 告诉你在 EXPLAIN 中复合索引”吃”到了第几列——值越大说明索引用得越充分,但需要会计算列长的”单价”才能读懂它。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容