MySQL key_len 大小的计算

2024-04-13 08:32

本文主要是介绍MySQL key_len 大小的计算,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景:

      当用Explain查看SQL的执行计划时,里面有列显示了 key_len 的值,根据这个值可以判断索引的长度,在组合索引里面可以更清楚的了解到了哪部分字段使用到了索引。

环境:

CREATE TABLE `tmp_0612` (`id` int(11) NOT NULL,`name` varchar(10) DEFAULT NULL,`age` int(11) DEFAULT NULL,`address` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

插入数据:

insert into tmp_0612 values(1,'a',11,'hz'),(2,'b',22,'gz'),(3,'c',33,'aa');

创建索引:

alter table tmp_0612 add index idx_name(name);
alter table tmp_0612 add index idx_age(age);

测试:

explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 33      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

从上面的执行计划可知,索引的长度是33。比预想的30(10*3(utf8))要高出3字节,为什么呢?进一步测试:

修改name 成 not null

alter table tmp_0612 modify name varchar(10) not null;

再看执行计划:

explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 32      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

发现上面的执行计划和第一次的有区别(key_len),经过多次测试,发现字段允许NULL的会多出一个字节。NULL是需要一个标志位的,占用1个字符。那还有2个字节怎么算?这里想到的是会不会和 多字节字符集 相关?那改字符集试试:


alter table tmp_0612 convert to charset latin1;explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 12      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

发现还是多了2个字节,看来和多字节字符集没有关系了。那会不会和 变长字段 有关系?再试试:


alter table tmp_0612 convert to charset utf8;alter table tmp_0612 modify name char(10) not null;explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 30      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

和预料的一样了,是30=10*3。到这里相信大家都已经很清楚了,要是还比较模糊就看反推到33字节。

改成允许NULL,应该会变成31。


alter table tmp_0612 modify name char(10);explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 31      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

改成变长字段类型,应该会变成33。


alter table tmp_0612 modify name varchar(10);explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 33      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

改成单字节字符集,还是还是需要额外的3字节(1:null ;变长字段:2),和字符集无关。


alter table tmp_0612 convert to charset latin1;explain select * from tmp_0612 where name ='a';
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys | key      | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_name      | idx_name | 13      | const |    1 | Using index condition |
+----+-------------+----------+------+---------------+----------+---------+-------+------+-----------------------+

反推上去都和预测的一样。

其他测试:


explain select * from tmp_0612 where age = 11;
+----+-------------+----------+------+---------------+---------+---------+-------+------+-------+
| id | select_type | table    | type | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+----------+------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_age       | idx_age | 5       | const |    1 | NULL  |
+----+-------------+----------+------+---------------+---------+---------+-------+------+-------+alter table tmp_0612 modify age int not null;explain select * from tmp_0612 where age = 11;
+----+-------------+----------+------+---------------+---------+---------+-------+------+-------+
| id | select_type | table    | type | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+----------+------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | tmp_0612 | ref  | idx_age       | idx_age | 4       | const |    1 | NULL  |
+----+-------------+----------+------+---------------+---------+---------+-------+------+-------+

int 是占4个字符的,上面key_len的也是符合预期。关于组合索引的,可以自己去测试玩。

总结:

      变长字段需要额外的2个字节,固定长度字段不需要额外的字节。而null都需要1个字节的额外空间,所以以前有个说法:索引字段最好不要为NULL,因为NULL让统计更加复杂,并且需要额外一个字节的存储空间。这个结论在此得到了证实。


key_len的长度计算公式:varchr(10)变长字段且允许NULL      : 10*(Character Set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
varchr(10)变长字段且不允许NULL    : 10*(Character Set:utf8=3,gbk=2,latin1=1)+2(变长字段)char(10)固定字段且允许NULL        : 10*(Character Set:utf8=3,gbk=2,latin1=1)+1(NULL)
char(10)固定字段且允许NULL        : 10*(Character Set:utf8=3,gbk=2,latin1=1)



这篇关于MySQL key_len 大小的计算的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/899636

相关文章

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

MySQL中On duplicate key update的实现示例

《MySQL中Onduplicatekeyupdate的实现示例》ONDUPLICATEKEYUPDATE是一种MySQL的语法,它在插入新数据时,如果遇到唯一键冲突,则会执行更新操作,而不是抛... 目录1/ ON DUPLICATE KEY UPDATE的简介2/ ON DUPLICATE KEY UP

MySQL分库分表的实践示例

《MySQL分库分表的实践示例》MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备... 目录一、分库分表的触发条件1.1 数据量阈值1.2 并发压力二、分库分表的核心技术模块2.1 水平分

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

MySQL 表空却 ibd 文件过大的问题及解决方法

《MySQL表空却ibd文件过大的问题及解决方法》本文给大家介绍MySQL表空却ibd文件过大的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录一、问题背景:表空却 “吃满” 磁盘的怪事二、问题复现:一步步编程还原异常场景1. 准备测试源表与数据

Mac电脑如何通过 IntelliJ IDEA 远程连接 MySQL

《Mac电脑如何通过IntelliJIDEA远程连接MySQL》本文详解Mac通过IntelliJIDEA远程连接MySQL的步骤,本文通过图文并茂的形式给大家介绍的非常详细,感兴趣的朋友跟... 目录MAC电脑通过 IntelliJ IDEA 远程连接 mysql 的详细教程一、前缀条件确认二、打开 ID