MYSQL的explain执行计划中key_len计算方式

零、引言

在执行计划中,key_len指的是使用的索引字段的长度。计算方式是将使用索引字段的长度相加,然后再加上索引类型所占用的字节数。这个值对于MySQL优化器来说非常重要,因为它可以帮助MySQL选择最优的索引。

如果key_len的值很大,那么查询就会变得很慢,因为MySQL需要读取更多的数据。因此,在创建索引时,要尽量减小key_len的值,以提高查询性能(key_len越小,说明索引效果越好)

一、创建测试表和数据

在mysql库中,新建测试表key_len_test(表采用的utf8mb4字符集

CREATE TABLE `key_len_test` (
  `id`                  bigint(32)          unsigned AUTO_INCREMENT COMMENT '主键ID',
  `age`                 bigint              not null comment '年龄',
  `name`                char(32)            NOT NULL COMMENT '姓名',
  `sex`                 char(32)            NULL COMMENT '性别 1-男、2-女',
  `address`             varchar(32)         NULL COMMENT '地址',
  `total_score`         bigint              NULL COMMENT '总成绩',
  `ext`                 varchar(512)        NOT NULL DEFAULT '' COMMENT '扩展信息',
  `create_time`         datetime            NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
  `update_time`         timestamp           NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录最后更新时间',
  PRIMARY KEY (`id`),  
  KEY `idx_age` (`age`),
  KEY `idx_name` (`name`),
  KEY `idx_sex` (`sex`),
  KEY `idx_address` (`address`),
  KEY `idx_total_score` (`total_score`),
  KEY `idx_ext` (`ext`),
  KEY `idx_created_time` (`create_time`),
  KEY `idx_modified_time` (`update_time`)
)  COMMENT='key_len测试表';

向表中插入一些数据,用于演示

insert into key_len_test(age, name, sex, address, total_score, ext) values (18, '张三', '1', '北京海淀', 600, '高考成绩');
insert into key_len_test(age, name, sex, address, total_score, ext) values (18, '李四', '2', '上海浦东', 590, '高考成绩');
insert into key_len_test(age, name, sex, address, total_score, ext) values (18, '王五', '1', '天津河西', 610, '高考成绩');
insert into key_len_test(age, name, sex, address, total_score, ext) values (18, '小明同学', '1', '北京朝阳', 700, '高考成绩');

上面的表比较简单,有一个主键索引id和其他普通辅助索引,下面以这个表为例,看下key_len如何计算的。

二、计算key_len

1、单列索引key_len计算

图1

 有上图可知,key_len的长度为128,那128怎么计算的呢?

在创建表时 字段 “name” 语句 = `name` char(32) NOT NULL COMMENT '姓名',表用的utf8mb4字符集,utf8字符集和utf8mb4字符集分别占3字节和4字节,字段name为char(32),所以128来源 key_len= 32 * 4 =128。若字符集为utf8,则key_len= 32 * 3 = 96。 

查看表创建脚本和表字符集:show create table key_len_test;

查看表中索引:show index from key_len_test;

a.比对not null 与 null 在计算key_len的不同

图2

 字段 `sex`  char(32) NULL COMMENT '性别 1-男、2-女' 与图1中的name对比 是not null 和null的区别,因为NULL 需要额外一个字节标记为空129 = 32 * 4 + 1 = 129。

b.比对 varchar 与 char 在计算key_len的不同

图3

 与图2比,字段 `address` varchar(32) NULL COMMENT '地址'与 字段sex 只是类型不同,但key_len却不一样,原因 是 varchar类型字段 为变长字段类型,还有长度信息,需占2个字节 。所以 131 = 32 * 4 + 2 + 1 = 131。

同理,按照上面的方式可以验证其他类型单列索引的key_len情况。

字段类型索引字节(NOT NULL)字节(NULL) 说明
tinyint12因为NULL 需要额外一个字节标记为空
int45因为NULL 需要额外一个字节标记为空
bigint89因为NULL 需要额外一个字节标记为空
smallint23因为NULL 需要额外一个字节标记为空
mediumint34因为NULL 需要额外一个字节标记为空
float45因为NULL 需要额外一个字节标记为空
double89因为NULL 需要额外一个字节标记为空
date34因为NULL 需要额外一个字节标记为空
timestamp45因为NULL 需要额外一个字节标记为空
datetime56因为NULL 需要额外一个字节标记为空,在5.6中字段长度是5个字节

以上只是针对不受字符集影响的常见字段类型 索引所占的字节的总结。CHARVARCHARTEXT、BLOB 类型 会因表字符集不同,所占字节数不同。

2、联合索引key_len计算

a.将表中name和sex字段索引删除,添加联合索引key(name,sex)

-- 删除name和sex单列索引
alter table key_len_test drop index idx_name,drop index idx_sex ;
-- 添加name和sex联合索引
alter table key_len_test add index idx_name_sex(name,sex);

执行单个字段sql查询

 执行两个字段sql查询

 以上两个sql执行计划中key_len和ref不同,第二个sql的key_len为257 ,怎么计算出来的呢。

在建表脚本中name 为char(32)  NOT NULL 、sex 为char(32)  NULL ,从上面知道NULL 需1个字节来标识,所以第二条sql key_len = 32 * 4 + 32 * 4 + 1 = 257。通过计算两个字段索引都完全里使用。

b.添加时间字段联合索引

-- 添加name和create_time联合索引
alter table key_len_test add index idx_created_time_name(create_time,name);

执行联合索引sql查询时间范围

图 4 时间范围执行结果

 指定时间执行联合索引sql查询

图5 执行时间执行结果

 根据执行结果可以看出,两个执行结果都用到了联合索引idx_created_time_name,但两个key_len是不一样的,是否充分利用到索引,从结果分析 图4未充分使用索引,因为 mysql 5.6后 datetime 占5字节,说明图4 只用到了联合索引idx_created_time_name中created_time字段。而图5采用的是时间定值 key_len= 32 * 4 + 5 = 133,充分利用了联合索引。

c.char字段和varchar字段组成的联合索引

执行联合索引查询sql

 根据结果来看,key_len为260。索引是否完全用到了呢?那么key_len是如何计算的呢?

`sex`  char(32) NULL COMMENT '性别 1-男、2-女',`address` varchar(32) NULL COMMENT '地址'

从2个字段的定义来看,2个允许NULL,一个char,一个varchar;

所以key_len= (32 * 4 + 1) + ( 32 * 4 + 2 + 1) =260,其中,+1是因为MySQL需要1个字节标识NULL,+2是因为address字段为varchar,是变长字段需要+2。所以完全用到了联合索引。

d.key_len使用索引的位置,是where 、order by、group by中的索引都关注呢还是只关注某一部分

1)order by 使用索引列

图6 order by使用联合索引列

2) group by 使用索引列

图7 group by使用联合索引列

 key_len只指示了where中用于条件过滤时被选中的索引列,是不包含order by、group by这一部分被选中的索引列的

三、总结

1、注意点

  1. NULL 和NOT NULL 的所占字节不同,NULL 标记占1字节。NOT NULL标记 不占字节。
  2. 变长数据类型,如varchar 、text、blob,还有长度信息,占2个字节。
  3. char、varchar、blob、text等字符集来说,key_len的长度还和字符集有关。
    • 在latin1字符集中,一个字符占用1个字节

    • 在GBK字符集中,一个字符占用2个字节

    • 在utf8字符集中,一个字符占用3个字节

    • 在utf8mb4字符集中,一个字符占用4个字节。

      注意:当为 BLOB、TEXT 或其变体类型的列创建索引时,必须指定索引长度。这是因为这些数据类型的值可以非常大,并且 MySQL 需要知道要从哪里开始和结束索引。

      如果在创建索引时没有指定长度,就会出现 "BLOB/TEXT column 'column_name' used in key specification without a key length" 的错误提示。

2、在MySQL数据库中,常见类型执行计划中的key_len长度计算

索引列类型latin1GBKutf8utf8mb4key_len计算说明
char(30)123430 * n + 1

n:1字符占用n字节,不同字符集不同

+1:允许为NULL 占1字节

varchar(30)123430 * n + 2 + 1

n:1字符占用n字节,不同字符集不同

+1:允许为NULL 占1字节

+2:变长数据类型需2字节占位

blob(30)123430 * n + 2 + 1

n:1字符占用n字节,不同字符集不同

+1:允许为NULL 占1字节

+2:变长数据类型需2字节占位

text(30)123430 * n + 2 + 1

n:1字符占用n字节,不同字符集不同

+1:允许为NULL 占1字节

+2:变长数据类型需2字节占位

int44444 + 1+1:允许为NULL 占1字节
bigint48888 + 1+1:允许为NULL 占1字节

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值