从多个索引中读取MySQL?

时间:2012-04-30 20:10:58

标签: mysql indexing explain

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

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`);

现在让我们再次运行前一个select语句并告诉它使用新索引。

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?

非常感谢任何见解。

编辑:好的,还有另一个例子。这个是原始表定义(因此每列的索引)。

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 |
+----+-------------+-------+------+---------------+-------+---------+----------+------+-------------+

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

再次感谢

4 个答案:

答案 0 :(得分:4)

keyrefrows列更能说明这一点。在每种情况下,它们表明MySQL已选择索引,具有在该索引中查找的值,并且因此仅从表中检索一行。这就是你所追求的。

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

{p} The manual Using index Extra信息:

  

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

     

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

     

对于具有用户定义的聚簇索引的InnoDB表,即使Using index列中不存在Extra,也可以使用该索引。如果typeindexkeyPRIMARY,则属于这种情况。

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

稍微想一下索引是如何工作的。

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

您发出查询时,有几种方法可以为您提供数据。在test表和value列的简化案例中,类似于:

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

数据库query planner有两个选项:

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

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

    • 当你的桌子在磁盘上占用非常小的尺寸时;
    • 当您的查询要求查找test表中的总记录数的10%(数字10%非常近似时,请不要将其视为理所当然。)

    需要考虑的事项:

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

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

    因此,索引并不总是好的。

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

答案 3 :(得分:1)

5.0的新功能,MySQL可以在Index merge的表上使用多个索引,尽管它们不像多列covering indexes那么快(到目前为止),所以MySQL只会使用他们在特殊情况下。

因此,除了合并索引的情况,MySQL每个表只使用一个索引。

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

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

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

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

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

索引也耗尽内存。如果有足够的内存,正确配置的MySQL服务器将在内存中加载每个索引。这使得使用覆盖索引的选择超快。