索引 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 做什么?