MySQL索引Key Length详解

一、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说明
tinyint1允许 NULL 时 +1
smallint2允许 NULL 时 +1
int4int(10) 仍为 4
bigint8允许 NULL 时 +1
float4
double8
decimal(M,D)按精度计算见下文详细规则
DECIMAL 的 key_len 计算

MySQL 将 DECIMAL 以二进制打包存储,每 9 位十进制数占 4 字节,剩余位数分配如下:

剩余位数字节数
1–21
3–42
5–63
7–94

示例decimal(10,2)

  • 整数部分 8 位 → 4 字节
  • 小数部分 2 位 → 1 字节
  • key_len = 5

2.2 日期时间类型

类型key_len说明
date3
datetime5(MySQL 5.6+)/ 8(5.5 及更早)无小数秒时
timestamp4
time3

若带小数秒精度(如 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 则不加
字符集对计算的影响
字符集单字符最大字节数示例
latin11varchar(100) = 最大 100 字节
utf8 / utf8mb33varchar(100) = 最大 300 字节
utf8mb44varchar(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_code100×3 + 2302
deliver_station_no50×3 + 1151
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) utf8key_len 就是 50×3 = 150 字节。

正确理解:变长字符串(varchartext 等)需要额外的长度前缀(≤255 时为 1 字节,>255 时为 2 字节),以及可能的 NULL 标志位(1 字节)。

❌ 误区 3:int(10) 的 10 影响 key_len

错误理解int(10)int 占用更多空间。

正确理解int 类型固定 4 字节。括号内的数字仅为显示宽度(配合 ZEROFILL),不影响存储、索引和 key_len

七、总结

  1. key_len 是静态值:基于表定义计算,与实际数据内容无关。
  2. 字符串公式key_len = 定义最大字节长度 + 长度前缀(1/2)+ NULL 标志(0/1)
  3. 复合索引判断:通过 key_len 的累加值,可准确判断查询使用了复合索引的前几列。
  4. 长度前缀规则:最大字节长度 ≤255 用 1 字节,>255 用 2 字节。
  5. int(M)M 无影响:整数类型长度固定,括号内仅为显示宽度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝多芬也爱敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值