为什么在索引列上没有SELECT DISTINCT瞬时?

时间:2019-05-17 18:34:43

标签: mysql mariadb b-tree database-indexes

我有一个像这样的表,用于存储所运行的各种程序的配置。看起来像这样:

+--------------+---------------+------+-----+---------+-------+
| Field        | Type          | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| Date         | date          | YES  | MUL | NULL    |       |
| Program      | varchar(20)   | YES  | MUL | NULL    |       |
| ConfigFile   | int(11)       | YES  |     | NULL    |       |
| Parameter    | varchar(20)   | YES  |     | NULL    |       |
| Value        | varchar(20)   | YES  |     | NULL    |       |
+--------------+---------------+------+-----+---------+-------+

ConfigFile字段包含配置文件的编号-对于某些程序,可以选择多个配置文件。

它具有几个索引,如下所示:

+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lists |          1 | Date     |            1 | Date         | A         |     1108060 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Date     |            2 | Program      | A         |     1108060 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Date     |            3 | Parameter    | A         |     1108060 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Program  |            1 | Program      | A         |        4676 |     NULL | NULL   | YES  | BTREE      |         |               |
| lists |          1 | Program  |            2 | Parameter    | A         |      183706 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

现在让我们说我想知道给定程序的参数是什么。看来我应该可以执行以下操作:

SELECT DISTINCT Parameter FROM params WHERE Program = 'MyProgram';

这具有以下说明计划:

+----+-------------+--------+------------+------+----------------+---------+---------+-------+-----------+----------+--------------------------+
| id | select_type | table  | partitions | type | possible_keys  | key     | key_len | ref   | rows      | filtered | Extra                    |
+----+-------------+--------+------------+------+----------------+---------+---------+-------+-----------+----------+--------------------------+
|  1 | SIMPLE      | params | NULL       | ref  | Date,Program   | Program | 23      | const | 137203382 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+------+----------------+---------+---------+-------+-----------+----------+--------------------------+

Program有15种不同的选择,每个程序的Parameter可能有10到100个值。

了解数据库索引的工作原理后,我希望它可以立即完成。特别是,我希望基础数据结构是一个具有15个节点的二进制搜索树,我将搜索这些树以找到与我的程序相对应的树。找到程序后,它带我到第二个二叉搜索树,该树可能有100个或更少的节点,然后我只需遍历。

不过,当我实际上运行查询时,它花了几分钟时间。

对我来说,这表明二进制搜索树中可能存在相同值的多个副本,表的每个节点一个。这是正在发生的事情吗,如果是这样,我该怎么做以减轻这种情况?

我考虑过使用一个具有唯一三元组(日期,程序,参数)的表并具有关系,但是我不确定在这种情况下如何执行数据的批量插入。而且,如果我错了为什么这么慢,那当然也帮不上忙。

1 个答案:

答案 0 :(得分:1)

InnoDB的B + Tree二级索引不会以这种方式形成。这样想吧:

  1. 对于每一行,构造一个由ProgramParameterPK组成的字符串。
  2. 对这些字符串进行排序。
  3. 将它们放置在BTree中。

注意:没有暗示被Program分割。如果99.9%的程序属于程序5,该怎么办?那将是一个相当不平衡的BTree。对于一个罕见的查询很方便,但是对于大多数其他查询却比较慢。

使用平衡良好的B +树,您的查询必须:

  1. 钻取BTree来找到Program = 'MyProgram'的第一个“行”
  2. 在B + Tree的叶子节点上向前走,使用“ +”从一个叶子块移至下一个叶子块。
  3. 边走边跟踪每个新的Parameter
  4. Program = 'MyProgram'失败时退出。

注意:

    通过了解商品的订购方式,在我的第3步中轻松实现了
  • DISTINCT
  • “使用索引”表示索引正在“覆盖”-因为您只需要ProgramParameter(而这些是INDEX中的列)。 PK也隐式地可用于“覆盖”。
  • 您提供的15个不同意“ 4676”的基数。但这只是指出统计数据有时相差甚远。 (统计信息对此查询的优化没有影响。)
  

我考虑过一张带有唯一三元组(日期,程序,参数)的表

是的,拥有这样一个表将使您的查询运行更快。但这值得维护吗?

该表允许您执行的另一件事是将这3列标准化为单个MEDIUMINT UNSIGNED(仅3个字节),而不是现在平均行上可能使用了30个字节。同样,JOINs等的复杂性是否会超过收益?它将使磁盘占用空间减少约50%。