索引 VARCHAR 的 key_len
Indexed VARCHAR's key_len
-- 2 Moderators: please do not re-format my code
DROP TABLE IF EXISTS `t`;
CREATE TABLE `t` (
`v` VARCHAR(3) NOT NULL COLLATE 'latin1_general_ci'
, `c` CHAR(3) NOT NULL COLLATE 'latin1_general_ci'
, INDEX `IX_t_v` (`v`)
, INDEX `IX_t_c` (`c`)
)
;
INSERT INTO t
(v, c)
VALUES
('001', '001')
, ('002', '002')
, ('003', '003')
, ('004', '004')
;
EXPLAIN SELECT c FROM t WHERE c = '001';
EXPLAIN SELECT v FROM t WHERE v = '001';
解释命令给我:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t ref IX_t_c IX_t_c 3 const 1 100.00 Using index
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t ref IX_t_v IX_t_v 5 const 1 100.00 Using index
问题是:为什么第二个查询的 key_len 是 5?
基于 CHAR and VARCHAR Types,VARCHAR(3) NOT NULL 的存储要求应该是 4,所以我预计 key_len 也是一样的!
我在这里错过了什么?
谢谢。
P.S。 MySQL 5.7 和 MariaDB 10.1 为我提供了相同的结果。
https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L6790 说:
/**
@note
varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/
key_len
某种程度上是EXPLAIN
的杜撰。引擎可能以其他方式存储 VAR 列。同上,多字节字符集。而且还有NULL
的提示,貌似有两三种不同的方法。
我发现 key_len
仅对一件事有用:它使用了复合索引的多少列。 (随着 EXPLAIN FORMAT=JSON
的出现,此信息很容易获得。)
EXPLAIN 的 key_len 值
- n*m 字节,其中 n 是给定的限制 (varchar(n)),m 是给定字符集的每个字符的潜在字节数(latin1 为 1,utf8 为 3,utf8mb4 为 4)
- 如果
VAR
(varchar/varbinary) 加 2
- 为
NULL
加 1(即使 NULLness 可能使用单个位存储在引擎中并且 ROW_FORMAT
在使用中。)
相关长度问题:
- 例如utf8,并不总是每个字符使用3个字节,所以长度可以小于上面给出的n*m。
- VAR 字段仅采用所需的长度,加上长度指示符。
- 但是,当通过 MEMORY 实现临时 table 时,VARCHAR 变成全长 CHAR。 (此语句随 MySQL 8.0 一起消失。)因此,对于 one 情况,EXPLAIN 的 key_len 可能实际上反映了所使用的 space。
你用 key_len 做什么?
-- 2 Moderators: please do not re-format my code
DROP TABLE IF EXISTS `t`;
CREATE TABLE `t` (
`v` VARCHAR(3) NOT NULL COLLATE 'latin1_general_ci'
, `c` CHAR(3) NOT NULL COLLATE 'latin1_general_ci'
, INDEX `IX_t_v` (`v`)
, INDEX `IX_t_c` (`c`)
)
;
INSERT INTO t
(v, c)
VALUES
('001', '001')
, ('002', '002')
, ('003', '003')
, ('004', '004')
;
EXPLAIN SELECT c FROM t WHERE c = '001';
EXPLAIN SELECT v FROM t WHERE v = '001';
解释命令给我:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t ref IX_t_c IX_t_c 3 const 1 100.00 Using index
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t ref IX_t_v IX_t_v 5 const 1 100.00 Using index
问题是:为什么第二个查询的 key_len 是 5?
基于 CHAR and VARCHAR Types,VARCHAR(3) NOT NULL 的存储要求应该是 4,所以我预计 key_len 也是一样的!
我在这里错过了什么?
谢谢。
P.S。 MySQL 5.7 和 MariaDB 10.1 为我提供了相同的结果。
https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L6790 说:
/**
@note
varstring and blob keys are ALWAYS stored with a 2 byte length prefix
*/
key_len
某种程度上是EXPLAIN
的杜撰。引擎可能以其他方式存储 VAR 列。同上,多字节字符集。而且还有NULL
的提示,貌似有两三种不同的方法。
我发现 key_len
仅对一件事有用:它使用了复合索引的多少列。 (随着 EXPLAIN FORMAT=JSON
的出现,此信息很容易获得。)
EXPLAIN 的 key_len 值
- n*m 字节,其中 n 是给定的限制 (varchar(n)),m 是给定字符集的每个字符的潜在字节数(latin1 为 1,utf8 为 3,utf8mb4 为 4)
- 如果
VAR
(varchar/varbinary) 加 2
- 为
NULL
加 1(即使 NULLness 可能使用单个位存储在引擎中并且ROW_FORMAT
在使用中。)
相关长度问题:
- 例如utf8,并不总是每个字符使用3个字节,所以长度可以小于上面给出的n*m。
- VAR 字段仅采用所需的长度,加上长度指示符。
- 但是,当通过 MEMORY 实现临时 table 时,VARCHAR 变成全长 CHAR。 (此语句随 MySQL 8.0 一起消失。)因此,对于 one 情况,EXPLAIN 的 key_len 可能实际上反映了所使用的 space。
你用 key_len 做什么?