【问题标题】:Indexed VARCHAR's key_len索引 VARCHAR 的 key_len
【发布时间】:2020-03-15 14:57:09
【问题描述】:
-- 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 会是一样的!

我在这里错过了什么?

谢谢。

附: MySQL 5.7 和 MariaDB 10.1 给我的结果是一样的。

【问题讨论】:

    标签: mysql varchar explain


    【解决方案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
    */
    

    【讨论】:

    • 是否适用于所有 ROW_FOMAT 和引擎?还是在处理程序中?我听说如果表中任何列的最大 len 小于 256(或者可能是 128,以允许 NULL),长度为 1?
    • 它是sql 目录,所以它不在任何存储引擎中。它在 SQL 层中。
    • 由于必须在 SQL 层和 InnoDB 引擎层之间重写每一行,这并不能说明磁盘上使用的是 1 字节还是 2 字节。
    【解决方案2】:

    key_len 在某种程度上是对EXPLAIN 的捏造。引擎可能会以其他方式存储 VAR 列。同上,多字节字符集。还有NULL的指示,好像有两三种不同的方式。

    我看到key_len 只对一件事有用:它使用了多少个复合索引的列。 (随着EXPLAIN FORMAT=JSON 的出现,这些信息很容易获得。)

    EXPLAIN 的 key_len 值

    • n*m 字节,其中 n 是给定的限制 (varchar(n)),m 是给定字符集每个字符的潜在字节数(1 表示 latin1,3 表示 utf8,4 表示 utf8mb4)李>
    • 如果VAR(varchar/varbinary)则加 2
    • 对于 NULL 加 1(即使可能使用单个位存储 NULL 用于引擎和使用中的 ROW_FORMAT。)

    相关长度问题:

    • 例如,utf8 并不总是每个字符使用 3 个字节,因此长度可以小于上面给出的 n*m。
    • VAR 字段只取所需的长度,加上长度指示符。
    • 但是,当通过 MEMORY 实现临时表时,VARCHAR 会变成全长 CHAR。 (此语句在 MySQL 8.0 中消失。)因此,对于那种 one 情况,EXPLAIN 的 key_len 可能实际上反映了使用的空间。

    你用 key_len 做什么?

    【讨论】:

    • 谢谢。我还使用这些信息来了解使用了多列索引的多少部分。但是,额外的字节让我感到困惑,因为我的索引是:(VARCHAR(3) NOT NULL COLLATE latin1, TINYINT, TINYINT)。因此,当我看到 key_len=5 时,我认为使用了 2 部分索引并且无法理解这是怎么可能的,因为 TINYINT 不在 WHERE! 中。所以我问了。
    • @fifonik - A TINYINT NULL 有 key_len=2; TINYINT NOT NULL 计为 1。
    • 当然,在我的情况下它不是 NULL。手册中提到了 NULL 的存储要求,所以我知道这一点。因此,除了 VARCHAR 之外的所有内容都符合预期。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 2013-02-15
    • 1970-01-01
    • 2012-01-20
    相关资源
    最近更新 更多