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


暂无评论内容