【问题标题】:Make MySQL read from multiple indexes?让 MySQL 从多个索引中读取?
【发布时间】:2012-05-10 12:32:12
【问题描述】:

让我们从一个简单的例子开始:

CREATE TABLE `test` (
`id` INT UNSIGNED NOT NULL,
`value` CHAR(12) NOT NULL,
INDEX (`id`),
INDEX (`value`)
) ENGINE = InnoDB;

所以 2 列,均已编入索引。我认为这意味着 MySQL 将不再需要读取实际的表,因为所有数据都存储在索引中。

mysql> EXPLAIN SELECT id FROM test WHERE id = 1;
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+
|  1 | SIMPLE      | test  | ref  | id            | id   | 4       | const |    1 | Using index |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------------+

“使用索引”,非常好。据我了解,这意味着它是从索引中读取数据,而不是从实际表中读取数据。但我真正想要的是“价值”列。

mysql> EXPLAIN SELECT value FROM test WHERE id = 1;
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
|  1 | SIMPLE      | test  | ref  | id            | id   | 4       | const |    1 |       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+

嗯,这次没有“使用索引”。

我认为如果我添加一个涵盖两列的索引可能会有所帮助。

ALTER TABLE `test` ADD INDEX `id_value` (`id`,`value`);

现在让我们再次运行之前的选择语句并告诉它使用新索引。

mysql> EXPLAIN SELECT id, value FROM test USE INDEX (id_value) WHERE id = 1;
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
|  1 | SIMPLE      | test  | ref  | id_value      | id_value | 4       | const |    1 | Using index |
+----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+

赞美主,这是从索引中读取的。

但实际上我并不需要其他任何东西的组合索引。是否可以让 MySQL 从 2 个单独的索引中读取?

任何见解都将不胜感激。

编辑:好的,再举一个例子。这个是原始表定义(所以每列都有一个索引)。

mysql> EXPLAIN SELECT t1.value
    -> FROM test AS t1
    -> INNER JOIN test AS t2
    -> ON t1.id <> t2.id AND t1.value = t2.value
    -> WHERE t1.id = 1;
+----+-------------+-------+------+---------------+-------+---------+----------+------+-------------+
| id | select_type | table | type | possible_keys | key   | key_len | ref      | rows | Extra       |
+----+-------------+-------+------+---------------+-------+---------+----------+------+-------------+
|  1 | SIMPLE      | t1    | ref  | id,value      | id    | 4       | const    |    1 |             |
|  1 | SIMPLE      | t2    | ref  | value         | value | 12      | t1.value |    1 | Using where |
+----+-------------+-------+------+---------------+-------+---------+----------+------+-------------+

这当然必须从两个索引中读取(因为在连接条件中使用了两个字段)但它仍然从实际记录中读取数据,对吧?为什么它不只使用从索引中读取的数据?还是它实际上使用了这些数据而不说“使用索引”?

再次感谢

【问题讨论】:

  • 为什么要为您一开始没有选择的数据创建索引?索引占用(额外)空间,并且必须像表数据一样从磁盘读取(并且可以像表数据一样缓存在内存中)。
  • 我试图让我的例子尽可能简单。在实际应用中,我有充分的理由在那里建立索引。我添加的连接示例更接近我实际要做的事情,应该解释为什么“值”列有索引。

标签: mysql indexing explain


【解决方案1】:

5.0 的新功能,MySQL 可以在带有Index merge 的表上使用多个索引,尽管它们不如多列covering indexes 快(到目前为止),因此 MySQL 只会在特殊情况下使用它们.

所以,除了合并索引的情况,MySQL 只对每个表使用一个索引。

不要太害怕覆盖索引。他们可以履行双重职责。索引是最左边的前缀,因此您可以对最左边的列或第一和第二列使用多列索引,依此类推。

例如,如果您有多列索引 id_value (id,value),则可以删除索引 id (id),因为它是多余的。 id_value 索引也可以仅用于 id 列。

此外,对于 InnoDB,每个索引都会自动包含主键列,因此如果 id 是您的主键,则 value 上的索引提供与 (id 上的覆盖索引相同的好处, value)。

每个索引都会对索引列的插入和更新产生负面影响。有一个权衡,只有你(和一些测试)可以决定覆盖索引是否适合你。

删除对索引没有太大影响,因为它们只是“标记为删除”,并且只有在系统负载较低时才会被清除。

索引也会占用内存。给定足够的内存,正确配置的 MySQL 服务器会将每个索引加载到内存中。这使得使用覆盖索引的选择非常快。

【讨论】:

    【解决方案2】:

    想一想索引是如何工作的。

    假设您的test 表中有10k 条记录,value 列上有索引。当您使用数据填充表(或显式使用ANALYZE 命令)时,数据库会保留有关您的表和所有索引的统计信息。

    在您发出查询时,有多种方式可以向您提供数据。在test 表和value 列的非常简化的情况下,类似于:

    SELECT * FROM test WHERE value = 'a string';
    

    数据库query planner 有2个选项:

    1. 对整个表执行顺序扫描并过滤结果或
    2. 执行索引扫描以查找所需的数据条目。

    查询索引有一些性能损失,因为数据库必须在索引中寻找值。如果我们认为您的 B-tree 索引处于“良好状态”(即 balanced),那么您将在索引中最多 14 次查找中找到您的条目(如 2^14 > 10k,我希望我'这里没有弄错)。因此,为了向您提供具有a string 值的 1 行,数据库必须在索引中执行多达 14 次查找,并在您的表中执行 1 次额外查找。在不走运的情况下,这意味着系统将执行 15 次随机 I/O 操作以从您的磁盘中读取自定义数据部分。

    如果只有一个value 需要在索引中查找并且您的表非常大,那么索引操作将为您带来显着的性能提升。 但是有一点之后索引扫描变得更加昂贵,然后是直接的顺序扫描:

    • 当您的表在磁盘上占用的空间非常小时;
    • 当您的查询需要查找 test 表中记录总数的大约 10% 时(10% 的数字非常近似,不要想当然)。

    需要考虑的事项:

    • 数字数据类型的比较操作比字符串比较便宜得多;
    • 统计准确度;
    • 查询索引/表的频率,或在数据库的共享池中找到所需数据的概率。

    这些都会影响性能以及数据库选择交付数据的计划。

    所以,索引并不总是好的。

    回答您的to read from 2 separate indexes 问题:您正在寻找的功能称为Bitmap index,据我所知,它在MySQL 中不可用。

    【讨论】:

    • 哦,哇,我实际上认为索引总是存储在 RAM 中,这就是为什么我如此渴望从索引中读取数据的原因。 MySQL没有从我添加的连接示例中的索引中读取数据有什么原因吗?感谢您的回复!
    • 为什么不使用索引?正如@eggyal 提到的,key 列指定了 MySQl 使用的索引
    • 我知道它使用索引来执行连接。我只是想知道为什么在这种情况下没有“使用索引”。
    • 请关注link 并向下滚动到Using index。它解释了在哪些情况下Extra 列说Using index
    【解决方案3】:

    keyrefrows 列更能说明这一点。在每种情况下,它们都表明 MySQL 已经选择了一个索引,在该索引中有一个要查找的值,并且结果只从表中检索了一行。这就是你所追求的。

    在您的第二个查询中,MySQL 仍然需要从记录中检索 value,即使它已通过索引在 id 上找到记录。如果您的WHERE 标准基于value 进行查找,则该索引将被使用,并且无需检索记录。

    The manualUsing indexExtra信息:

    仅使用索引树中的信息从表中检索列信息,而无需执行额外的查找来读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。

    如果Extra 列也显示Using where,则表示索引正在用于执行键值查找。如果没有Using where,优化器可能会读取索引以避免读取数据行,但不会将其用于查找。例如,如果索引是查询的覆盖索引,优化器可能会扫描它而不使用它进行查找。

    对于具有用户定义聚集索引的InnoDB 表,即使Extra 列中不存在Using index,也可以使用该索引。如果typeindex 并且keyPRIMARY,就是这种情况。

    【讨论】:

      【解决方案4】:

      在您的第一个查询中,MySQL 说using index,因为它可以通过查看索引和单独的索引来回答您的查询。它不需要去表中查找id 列的对应值,因为这实际上与它已经在索引中得到的相同。

      在第二个查询中,MySQL 确实需要查看表以获取正确的值,但它仍在使用索引,正如您在 EXPLAIN 语句的 key 列中看到的那样。

      在第三个查询中,MySQL 再次不必再查看表,因为它需要回答您的查询的所有信息都在多列索引中。

      【讨论】:

        猜你喜欢
        • 2014-02-14
        • 2016-01-17
        • 2019-01-02
        • 2019-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多