1.定义:
没有区别,覆盖索引只是特定于具体select语录而言的联合索引。也就是说一个联合索引对于某个select语句,通过索引可以直接获取查询结果,而不再需要回表查询啦,就称该联合索引覆盖了这条select语句。
或者: MySQL可以利用索引返回SELECT 列表中的字段。而不必根据索引再次读取数据文件。包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index)。也就是平时所说的不需要回表操作。
或者:就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。
2.判断:
如果我们想查询所有在1969年出道的艺人的名字,可以运行下面的查询:
mysql> SELECT artist_id, name, founded
-> FROM artist
-> WHERE founded=1969;
我们的示例数据库比较小,这个表只有大约500 000行数据,然而我们还是可以借这个示例说明改进索引的影响。
在没有索引的情况下,这个查询耗时190毫秒。从查询执行计划中我们可以看出执行了一次全表扫描(使用第2章介绍的方法判断)。建议添加一个索引加以改进。请看下面的示例:
mysql> ALTER TABLE artist ADD INDEX (founded);
mysql> EXPLAIN SELECT
...
********************* 1. row ***********************
id: 1
select_type: SIMPLE
table: artist
type: ref
possible_keys: founded
key: founded
key_len: 2
ref: const
rows: 1035
Extra: Using where
在WHERE条件用到的founded列上添加索引之后,查询耗时减少到了5.9毫秒。这样简单的一个改动就可以让查询执行速度比原来提高了97%。然而,还可以创建一个使这条查询执行起来更快的索引:
mysql> ALTER TABLE artist
-> DROP INDEX founded,
-> ADD INDEX founded_name (founded,name);
mysql> EXPLAIN SELECT
...
********************* 1. row ***********************
id: 1 select_
type: SIMPLE
table: artist
type: ref
possible_keys: founded_name
key: founded_name
key_len: 2
ref: const
rows: 3696
Extra: Using where; Using index
使用多列索引之后,查询执行只需要1.2毫秒了。这比刚才的查询快了4倍,查询执行时间比第一次优化时又减少了80%,从总体来看我们节省了99%的查询执行时间。
尽管我们是通过多列索引来获得这样的性能提升的,但改善查询的真正因素并不是因为额外增加的列限制了访问的行数。使用第4章介绍的分析技术,我们可以看到这个多列索引只占用了2字节。可能你会认为这个多列索引中额外的列是无效的,但要注意在Extra这一列中显示了Using index。
当QEP在Extra列中显示Using index时,这并不意味着在访问底层表数据时使用到了索引,这表示只有这个索引才是满足查询所有要求的。这种索引可以为大型查询或者频繁执行的查询带来显著的性能提升,它被称为覆盖索引。
覆盖索引得名于它满足了查询中给定表用到的所有的列。想要创建一个覆盖索引,这个索引必须包含指定表上包括WHERE语句、ORDER BY语句、GROUP BY语句(如果有的话)以及SELECT语句中的所有列。
看了覆盖索引的介绍之后,你可能会好奇为什么artist_id这一列没有在索引中?对那些选择跳过第3章中介绍的索引的理论知识的读者来说,你们可能需要了解InnoDB的非主码索引的一个重要的特性。在InnoDB中,主码的值会被附加在非主码索引的每个对应记录后面,因此没有必要在非主码索引中指定主码。这一重要特性意味着InnoDB引擎中所有非主码索引都隐含主码列了。并且对于那些从MyISAM存储引擎转换过来的表,通常会在它们InnoDB表索引中将主码添加为最后一个元素。
技巧
有很多理由可以说服用户不要在SQL查询中使用SELECT *。上面提到的就是其中一个原因,说明如果在select语句中只包含那些真正需要的列,就能够通过创建合适的索引来获得更好的SQL优化。
然而我们的索引仅能使特殊定义查询性能大幅提升。如果我们想要通过添加某一类特定的艺术家来进一步限制查询,则结果如下:
mysql> EXPLAIN SELECT artist_id, name, founded
-> FROM artist
-> WHERE founded=1969
-> AND type='Person'\G
********************* 1. row ***********************
id: 1 select_
type: SIMPLE
table: artist
type: ref
possible_keys: founded_name
key: founded_name
key_len: 2
ref: const
rows: 3696
Extra: Using where
从结果可以看出我们使用了同样的索引,但却没有享受到覆盖索引带来的益处了。执行这个查询耗时5.4毫秒。我们可以根据新加的列来调整索引如下所示:
mysql> ALTER TABLE artist
-> DROP INDEX founded_name,
-> ADD INDEX founded_type_name(founded,type,name);
mysql> EXPLAIN SELECT
...
********************* 1. row ***********************
id: 1 select_
type: SIMPLE
table: artist
type: ref
possible_keys: founded_type_name
key: founded_type_name
key_len: 3
ref: const,const
rows: 1860
Extra: Using where; Using index
以上使用了修改之后的索引。我们还可以通过key_len的值为3来确定type列现在也是优化器限制的一部分,并且可以看出founded_type_name是覆盖索引。执行查询现在耗时1.3毫秒。
警告
创建这些索引只是用来描述确认覆盖索引的过程,但在生产环境中它们可能并不是理想的索引。由于数据集大小有限,我们在这些例子中使用了一个长字符列。随着数据容量的增加,尤其是超过内存和磁盘最大容量的时候,为一个大型列创建索引可能会对系统整体性能有影响。覆盖索引对于那些使用了很多较小长度的主码和外键约束的大型规范化模式来说是理想的优化方式。