让我们从一个简单的例子开始:
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 |
+----+-------------+-------+------+---------------+-------+---------+----------+------+-------------+
这必须从两个索引中读取(因为两个字段都在连接条件中使用)但是它仍然从实际记录中读取数据,对吧?为什么不只是使用它从索引中读取的数据?或者它是否实际使用该数据而不说“使用索引”?
再次感谢
答案 0 :(得分:4)
key
,ref
和rows
列更能说明这一点。在每种情况下,它们表明MySQL已选择索引,具有在该索引中查找的值,并且因此仅从表中检索一行。这就是你所追求的。
在第二个查询中,MySQL仍然需要从记录中检索value
,即使它已通过索引在id
上找到记录。如果您的WHERE
条件基于value
查找,则该索引将被使用,并且无需检索记录。
Using index
Extra
信息:
仅使用索引树中的信息从表中检索列信息,而无需执行额外的搜索来读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。
如果
Extra
列也显示Using where
,则表示该索引用于执行键值的查找。如果没有Using where
,优化器可能正在读取索引以避免读取数据行但不使用它进行查找。例如,如果索引是查询的覆盖索引,则优化程序可以扫描它而不使用它进行查找。对于具有用户定义的聚簇索引的
InnoDB
表,即使Using index
列中不存在Extra
,也可以使用该索引。如果type
为index
且key
为PRIMARY
,则属于这种情况。
答案 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
有两个选项:
查询索引会有一些性能损失,因为数据库必须在索引中寻找值。如果我们认为您的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_value
(id
,value
),则可以删除索引id
(id
)因为它是多余的。 id_value
索引也可以仅用于id列。
此外,对于InnoDB,每个索引都会自动包含主键列,因此如果id
是您的主键,value
上的索引提供与覆盖索引相同的好处(id
,value
)。
每个索引都会对插入以及对索引列的更新产生负面影响。有一个权衡,只有你(和一些测试)可以决定覆盖索引是否适合你。
删除对索引影响不大,因为它们只是“标记为删除”,并且只有在系统负载较低时才会被清除。
索引也耗尽内存。如果有足够的内存,正确配置的MySQL服务器将在内存中加载每个索引。这使得使用覆盖索引的选择超快。