一、key_len 核心概念
1.1 什么是 key_len?
- 定义:
EXPLAIN输出中key_len列表示 MySQL 根据表定义计算出的索引条目的最大可能字节长度。 - 本质:它是一个静态值,由列的数据类型、长度定义、字符集和是否允许
NULL决定,与表中实际存储的数据内容无关。 - 作用:通过
key_len可以判断复合索引中实际用到了前几列,进而评估索引使用是否充分。
重要纠正:
key_len不是 “实际存储的数据字节数 + 长度前缀”,而是基于列定义的最大容量计算出的上限值。
二、key_len 计算规则
2.1 数值类型(固定长度)
数值类型的长度仅由类型本身决定,括号中的显示宽度(如 int(10))不影响存储和 key_len。
| 数据类型 | key_len | 说明 |
|---|---|---|
tinyint | 1 | 允许 NULL 时 +1 |
smallint | 2 | 允许 NULL 时 +1 |
int | 4 | int(10) 仍为 4 |
bigint | 8 | 允许 NULL 时 +1 |
float | 4 | |
double | 8 | |
decimal(M,D) | 按精度计算 | 见下文详细规则 |
DECIMAL 的 key_len 计算
MySQL 将 DECIMAL 以二进制打包存储,每 9 位十进制数占 4 字节,剩余位数分配如下:
| 剩余位数 | 字节数 |
|---|---|
| 1–2 | 1 |
| 3–4 | 2 |
| 5–6 | 3 |
| 7–9 | 4 |
示例:decimal(10,2)
- 整数部分 8 位 → 4 字节
- 小数部分 2 位 → 1 字节
- key_len = 5
2.2 日期时间类型
| 类型 | key_len | 说明 |
|---|---|---|
date | 3 | |
datetime | 5(MySQL 5.6+)/ 8(5.5 及更早) | 无小数秒时 |
timestamp | 4 | |
time | 3 |
若带小数秒精度(如
datetime(3)),在基础长度上额外增加:精度 1–2 位加 1 字节,3–4 位加 2 字节,5–6 位加 3 字节。
2.3 字符串类型(重点)
字符串类型的 key_len 计算基于列定义的最大字节长度,而非实际存储值:
key_len = 列定义最大字节长度 + 长度前缀 + NULL 标志位
- 列定义最大字节长度 = 字符集单字符最大字节数 × 字符长度定义(N)
- 长度前缀:记录变长字段内容长度所需的字节
- 若 最大字节长度 ≤ 255:1 字节
- 若 最大字节长度 > 255:2 字节
- NULL 标志位:若列允许
NULL,额外加 1 字节;NOT NULL则不加
字符集对计算的影响
| 字符集 | 单字符最大字节数 | 示例 |
|---|---|---|
latin1 | 1 | varchar(100) = 最大 100 字节 |
utf8 / utf8mb3 | 3 | varchar(100) = 最大 300 字节 |
utf8mb4 | 4 | varchar(100) = 最大 400 字节 |
三、key_len 计算示例(基于列定义)
以下示例均基于列定义计算,与实际存储值无关。
示例 1:varchar(50) utf8 NOT NULL
- 数据部分最大:50 × 3 = 150 字节
- 长度前缀:150 ≤ 255 → 1 字节
- NULL 标志位:0 字节
- key_len = 150 + 1 = 151 字节
无论该列实际存储
'STATION_A'还是'北京仓库',EXPLAIN中的key_len均为 151。
示例 2:varchar(100) utf8 NOT NULL
- 数据部分最大:100 × 3 = 300 字节
- 长度前缀:300 > 255 → 2 字节
- NULL 标志位:0 字节
- key_len = 300 + 2 = 302 字节
示例 3:varchar(500) utf8 DEFAULT NULL
- 数据部分最大:500 × 3 = 1500 字节
- 长度前缀:1500 > 255 → 2 字节
- NULL 标志位:1 字节(允许 NULL)
- key_len = 1500 + 2 + 1 = 1503 字节
示例 4:char(10) utf8 NOT NULL
CHAR 为定长类型,不按实际内容缩短:
- 数据部分:10 × 3 = 30 字节
- 长度前缀:0 字节(定长无需前缀)
- NULL 标志位:0 字节
- key_len = 30 字节
四、复合索引的 key_len 计算
复合索引的 key_len 是查询实际使用到的各列定义最大长度之和。
4.1 索引示例
CREATE TABLE `order` (
`erp_org_code` varchar(100) CHARACTER SET utf8 NOT NULL,
`deliver_station_no` varchar(50) CHARACTER SET utf8 NOT NULL,
`create_time` datetime NOT NULL,
KEY `deliver_idx` (`erp_org_code`, `deliver_station_no`, `create_time`)
);
各列的理论 key_len:
| 列名 | 计算过程 | 单列 key_len |
|---|---|---|
erp_org_code | 100×3 + 2 | 302 |
deliver_station_no | 50×3 + 1 | 151 |
create_time | 定长(假设 5) | 5 |
4.2 判断索引使用情况
-- 查询 1:只用到了第一列
WHERE erp_org_code = 'xxx';
-- key_len = 302
-- 查询 2:用到了前两列
WHERE erp_org_code = 'xxx' AND deliver_station_no = 'yyy';
-- key_len = 302 + 151 = 453
-- 查询 3:用到了全部三列
WHERE erp_org_code = 'xxx' AND deliver_station_no = 'yyy' AND create_time = '...';
-- key_len = 302 + 151 + 5 = 458
注意:如果
WHERE erp_org_code = 'xxx'中的'xxx'实际只有 3 个字节,key_len仍然是 302,因为EXPLAIN基于列定义而非实际值。
五、key_len 在优化中的意义
5.1 诊断索引使用充分性
-- 假设索引为 idx(a, b, c)
-- 若查询 WHERE a='xxx',key_len 显示为 302
-- 若查询 WHERE a='xxx' AND b='yyy',key_len 显示为 453
-- 通过对比即可判断 MySQL 是否使用了更多的索引列
5.2 识别索引失效
-- 执行计划显示 key_len = 8(仅主键)
-- 说明 WHERE 条件未能有效利用二级索引,需要检查:
-- 1. 是否存在隐式类型转换
-- 2. 是否使用了函数或运算
-- 3. 是否违反了最左前缀原则
六、常见误区澄清
❌ 误区 1:key_len 等于实际存储的数据长度
错误理解:实际存了 'STATION_A'(9 字节),所以 key_len = 9 + 1 = 10。
正确理解:key_len 基于列定义的最大容量计算。varchar(50) utf8 NOT NULL 永远是 151 字节,与实际存储值无关。
❌ 误区 2:key_len 等于字段定义的最大长度(忘记长度前缀)
错误理解:varchar(50) utf8 的 key_len 就是 50×3 = 150 字节。
正确理解:变长字符串(varchar、text 等)需要额外的长度前缀(≤255 时为 1 字节,>255 时为 2 字节),以及可能的 NULL 标志位(1 字节)。
❌ 误区 3:int(10) 的 10 影响 key_len
错误理解:int(10) 比 int 占用更多空间。
正确理解:int 类型固定 4 字节。括号内的数字仅为显示宽度(配合 ZEROFILL),不影响存储、索引和 key_len。
七、总结
key_len是静态值:基于表定义计算,与实际数据内容无关。- 字符串公式:
key_len = 定义最大字节长度 + 长度前缀(1/2)+ NULL 标志(0/1) - 复合索引判断:通过
key_len的累加值,可准确判断查询使用了复合索引的前几列。 - 长度前缀规则:最大字节长度 ≤255 用 1 字节,>255 用 2 字节。
int(M)的M无影响:整数类型长度固定,括号内仅为显示宽度。


3349

被折叠的 条评论
为什么被折叠?



