1

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

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

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

再次感谢

4

4 回答 4

4

The key, ref and rows columns are more telling for this purpose. In each case, they indicate that MySQL has selected an index, has a value to lookup in that index, and is retrieving only one row from the table as a result. This is what you were after.

In your second query, MySQL still needs to retrieve the value from the record even though it has located the record on id via an index. If your WHERE criterion looked up based on value, then that index would have been used and there would have been no need to retrieve the record.

The manual on Using index Extra information:

The column information is retrieved from the table using only information in the index tree without having to do an additional seek to read the actual row. This strategy can be used when the query uses only columns that are part of a single index.

If the Extra column also says Using where, it means the index is being used to perform lookups of key values. Without Using where, the optimizer may be reading the index to avoid reading data rows but not using it for lookups. For example, if the index is a covering index for the query, the optimizer may scan it without using it for lookups.

For InnoDB tables that have a user-defined clustered index, that index can be used even when Using index is absent from the Extra column. This is the case if type is index and key is PRIMARY.

于 2012-04-30T20:15:39.977 回答
2

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

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

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

于 2012-04-30T20:18:36.553 回答
1

5.0 的新功能,MySQL 可以通过Index merge在一张表上使用多个索引,尽管它们(到目前为止)不如多列覆盖索引那么快,因此 MySQL 只会在特殊情况下使用它们。

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

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

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

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

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

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

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

于 2012-04-30T20:53:46.763 回答
1

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

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

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

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

数据库query planner有 2 个选项:

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

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

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

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

需要考虑的事项:

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

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

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

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

于 2012-04-30T20:46:35.313 回答