-
在计算key_len时,常见类型占用的字节数
数值类型占用字节数列表
数据类型 占用字节数 TINYINT 1 byte SMALLINT 2 bytes MEDIUMINT 3 bytes INT, INTEGER 4 bytes BIGINT 8 bytes FLOAT§ 4 bytes if 0 explain select * from test_int where id4 = 1;
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_int | NULL | ref | idx_test_int_id4 | idx_test_int_id4 | 8 | const | 1 | 100.00 | NULL |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--索引字段为double,key_len值为8
mysql> explain select * from test_int where id5 = 1;
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_int | NULL | ref | idx_test_int_id5 | idx_test_int_id5 | 8 | const | 1 | 100.00 | NULL |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--索引字段为decimal(19,8),key_len值为9,key_len=(4+1)(11整数位,分为9位整数用4个字节,剩余2位用1个字节及4+1)+4(8个小数位用4个字节)
mysql> explain select * from test_int where id6 = 1;
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_int | NULL | ref | idx_test_int_id6 | idx_test_int_id6 | 9 | const | 1 | 100.00 | NULL |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--索引字段为big,根据公式计算 (10+7)/8 bytes近似值为2,执行计划中key_len值为2,
mysql> explain select * from test_int where id7 = b'1000001110';
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_int | NULL | ref | idx_test_int_id7 | idx_test_int_id7 | 2 | const | 1 | 100.00 | NULL |
+----+-------------+----------+------------+------+------------------+------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)日期和时间类型占用字节数列表
对于TIME, DATETIME和TIMESTAMP列,MySQL 5.6.4之前创建的表所需的存储空间与5.6.4以后创建的表不同。这是由于5.6.4中的功能更新,允许这些类型具有小数部分,这需要从0到3个字节。
数据类型 MySQL 5.6.4之前的存储要求 MySQL 5.6.4之后的存储要求 YEAR 1 byte 1 byte DATE 3 bytes 3 bytes TIME 3 bytes 3 bytes +小数秒存储 DATETIME 8 bytes 5 bytes +小数秒存储 TIMESTAMP 4 bytes 4 bytes +小数秒存储 MySQL5.6.4及之后版本,TIME、DATETIME、TIMESTAMP这几种类型添加了对毫秒、微妙的支持。由于毫秒、微秒都不到1秒,所以也被称为小数秒,MySQL最多支持6位小数秒的精度,比如DATETIME(0)表示精确到秒,DATETIME(3)表示精确到毫秒,DATETIME(5)表示精确到微秒。所以如果你在使用TIME、DATETIME、TIMESTAMP这几种类型的时候精确到了小数秒,那么需要额外的存储空间,不同的小数秒精度需要的存储空间不同,如下表:
小数秒精度 存储要求 0 0 bytes 1, 2 1 byte 3, 4 2 bytes 5, 6 3 bytes --创建测试表
create table test_date (dt date not null,dt2 time not null,dt3 time(1) not null,dt4 datetime(3) not null,dt5 timestamp(5) not null);
insert into test_date values(now(),CURRENT_TIMESTAMP,CURRENT_TIMESTAMP(1),CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(5));
--创建索引
create index idx_test_date_dt on test_date(dt);
create index idx_test_date_dt2 on test_date(dt2);
create index idx_test_date_dt3 on test_date(dt3);
create index idx_test_date_dt4 on test_date(dt4);
create index idx_test_date_dt5 on test_date(dt5);
--索引字段为date,key_len值为3
mysql> explain select * from test_date where dt explain select * from test_date where dt2 explain select * from test_date where dt3 explain select * from test_date where dt4 explain select * from test_date where dt5 < '2024-01-02 17:10:38';
+----+-------------+-----------+------------+-------+-------------------+-------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+-------------------+-------------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | test_date | NULL | range | idx_test_date_dt5 | idx_test_date_dt5 | 7 | NULL | 1 | 100.00 | Using index condition |
+----+-------------+-----------+------------+-------+-------------------+-------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)explain select * from test_date where dt5 < '2024-01-02 17:10:38';
字符类型占用字节数列表
在下表中,M表示声明的列长度,对于非二进制字符串类型以字符为单位,对于二进制字符串类型以字节为单位。L表示给定字符串值的实际字节长度。
数据类型 存储要求 CHAR(M) 紧凑的InnoDB行格式优化了可变长度字符集的存储。参见COMPACT行格式存储特性。否则,M × w字节, explain select * from test_var where var4 ='a';
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_var | NULL | ref | idx_test_var_var4 | idx_test_var_var4 | 302 | const | 1 | 100.00 | NULL |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--索引字段为VARBINARY(6),key_len值为8及为6+2
mysql> explain select * from test_var where var5 ='a';
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | test_var | NULL | ref | idx_test_var_var5 | idx_test_var_var5 | 8 | const | 1 | 100.00 | Using index condition |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为VARBINARY(100),key_len值为102及为100+2
mysql> explain select * from test_var where var6 ='a';
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
| 1 | SIMPLE | test_var | NULL | ref | idx_test_var_var6 | idx_test_var_var6 | 102 | const | 1 | 100.00 | Using index condition |
+----+-------------+----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
验证TINYBLOB、 TINYTEXT、BLOB、 TEXT、MEDIUMBLOB、 MEDIUMTEXT、 LONGBLOB、LONGTEXT
--创建测试表,验证TINYBLOB、 TINYTEXT、BLOB、 TEXT、MEDIUMBLOB、 MEDIUMTEXT、 LONGBLOB、LONGTEXT
create table test_var2 (var TINYBLOB not null,var2 TINYTEXT not null,var3 BLOB not null,var4 TEXT not null,var5 MEDIUMBLOB not null,var6 MEDIUMTEXT not null,var7 LONGBLOB not null,var8 LONGTEXT not null);insert into test_var2 values('abc','abc','abc','abc','abc','abc','abc','abc');
insert into test_var2 values('abcde','abcde','abcde','abcde','abcde','abcde','abcde','abcde');
--创建索引
create index idx_test_var2_var on test_var2(var(10));
create index idx_test_var2_var2 on test_var2(var2(10));
create index idx_test_var2_var3 on test_var2(var3(10));
create index idx_test_va2r_var4 on test_var2(var4(10));
create index idx_test_var2_var5 on test_var2(var5(10));
create index idx_test_var2_var6 on test_var2(var6(10));
create index idx_test_var2_var7 on test_var2(var7(10));
create index idx_test_va2r_var8 on test_var2(var8(10));
--索引字段为TINYBLOB且给前10个字节创建索引,key_len值为12及10+2
mysql> explain select * from test_var2 where var='ab';
+----+-------------+-----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_var2_var | idx_test_var2_var | 12 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+-------------------+-------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为TINYTEXT且给前10个字符创建索引,key_len值为32及10*3+2
mysql> explain select * from test_var2 where var2='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_var2_var2 | idx_test_var2_var2 | 32 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为BLOB且给前10个字节创建索引,key_len值为12及10+2
mysql> explain select * from test_var2 where var3='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_var2_var3 | idx_test_var2_var3 | 12 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为TEXT且给前10个字符创建索引,key_len值为32及10*3+2
mysql> explain select * from test_var2 where var4='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_va2r_var4 | idx_test_va2r_var4 | 32 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为MEDIUMBLOB且给前10个字节创建索引,key_len值为12及10+2
mysql> explain select * from test_var2 where var5='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_var2_var5 | idx_test_var2_var5 | 12 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为MEDIUMTEXT且给前10个字符创建索引,key_len值为32及10*3+2
mysql> explain select * from test_var2 where var6='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_var2_var6 | idx_test_var2_var6 | 32 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为LONGBLOB且给前10个字节创建索引,key_len值为12及10+2
mysql> explain select * from test_var2 where var7='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_var2_var7 | idx_test_var2_var7 | 12 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
--索引字段为LONGTEXT且给前10个字符创建索引,key_len值为32及10*3+2
mysql> explain select * from test_var2 where var8='ab';
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
| 1 | SIMPLE | test_var2 | NULL | ref | idx_test_va2r_var8 | idx_test_va2r_var8 | 32 | const | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
通过上面的验证可知当索引字段为字符串时,key_len值受如下
- char、varchar、text、LONGTEXT、MEDIUMTEXT、TINYTEXT字段类型受字符集影响
- varchar 、VARBINARY、TINYBLOB、 TINYTEXT、BLOB、 TEXT、MEDIUMBLOB、 MEDIUMTEXT、 LONGBLOB、LONGTEXT变长字段,通过验证发现,在计算索引长度的时候,统一加2字节存储该变长列的实际长度,与其他列长度或者实际长度没有关系
索引字段为字符串时,索引列数据类型本身占用的空间跟字符集有关
常用的字符编码占用字节数量如下:
字符编码 占用字节数 GBK 2 UTF8 3 ISO8859-1 1 GB2312 2 UTF-16 2 下面测试数据库字符编码格式为UTF8占3个字节,目前测试发现只有char、varchar、text、LONGTEXT、MEDIUMTEXT、TINYTEXT字段类型受字符集影响
比如char(M)字段占用空间:M*Maxlen(字符集占用字节数),详细验证步骤可参考字符类型占用字节数列表的验证步骤
注意:MySQL5.0版本以上,字符串定义列的长度单位为字符
索引字段列为NULL时,key_len计算时,需要加1;索引字段为NOT NULL时只计算索引列数据类型本身占用的空间
--创建测试表
create table test_null (id int,name1 varchar(10),name2 varchar(10) not null);
insert into test_null values(1,'wang','wang');
insert into test_null values(2,'li','wang');
create index idx_test_null_name1 on test_null(name1);
create index idx_test_null_name2 on test_null(name2);
--索引字段name1为空时,使用索引idx_test_null_name1,key_len=10*3+2(可变长字段的长度)+1(索引字段为NULL)
mysql> explain select * from test_null where name1 = '1';
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_null | NULL | ref | idx_test_null_name1 | idx_test_null_name1 | 33 | const | 1 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
--索引字段name2不为空时,使用索引idx_test_null_name2,key_len=10*3+2(可变长字段的长度)
mysql> explain select * from test_null where name2 = '2';
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | test_null | NULL | ref | idx_test_null_name2 | idx_test_null_name2 | 32 | const | 1 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)