索引是存储引擎用于快速查找记录的一种数据结构。因此良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。在数据量较小且负载较低时,不恰当的索引对性能的影响可能不明显,但当数据量逐渐增大时,性能则会急剧下降。索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高几个数量级,“最优”的索引有时比一个“好的”索引性能要好两个数量级。
索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为mysql只能高效的使用索引的最左前缀列。创建一个包含列个列的索引,和创建两个只包含一列的索引是大不相同的。
在mysql中,索引是存储引擎层而不是服务器层实现的。不同的存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持同一种类型的索引,其底层的实现也不能不同。
一、索引类型
1、B-Tree索引
大多数mysql存储引擎都支持B-Tree索引。存储引擎以不同的方式使用B-Tree索引,性能也各不相同。例如,MyISAM使用前缀压缩技术使得索引更小,但InnoDB则按照原数据格式进行存储。再如MyISAM索引通过数据的物理位置引用被索引的行,而InnoDB则根据主键引用被索引的行。
B-Tree通常意味着所有的值都按顺序存储的,并且每一个叶子页到根的距离相同。B-Tree索引能够加快访问数据的速度,因为存储引擎不在需要进行全表扫描来获取需要的数据,而是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到适合的指针进入下层子节点,这些指针实际上定义了子节点页中值的下限和上限。最终存储引擎要么找到对应的值,要么改记录不存在。
叶子节点比较特别,他们的指针指向的是被索引的数据,而不是其他的节点页。在根节点和叶子节点之间可能有很多层节点页。树的深度和表的大小直接相关。B-Tree对索引列是顺序组织存储的,所以很适合查找范围数据。例如,在一个基于文本域的索引树上,按字母顺序连续的值进行查找是非常适合的,所以像“找出所有以I到K开头的名字”这样的查找效率会非常高。
B-Tree索引适用于全键值、键值范围或键前缀查找。其中键前缀查找只适用于根据最前缀的查找。前面所述的索引对如下类型的查询有效:
①全值匹配。索引中所有列进行匹配;
②匹配最左前缀;
③匹配列前缀。在满足最左前缀的基础上匹配最左列的开头部分;
④配置范围值。最左列的某一范围内的数据;
⑤精确匹配最左列并范围匹配最左列的相邻列;
⑥只访问索引的查询。需要查询的字段在索引列中都包含,所以在索引的叶子节点上就可以获取到这些列的值,无需查询数据行。
因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查找中的order by操作。一般来说,如果b-tree可以按照某种方式查找到值,那么也可以按照某种方式用于排序。所以,如果order by子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。
下面是一些关于b-tree索引的限制:
①如果不是按照索引的最左列开始查找,则无法使用索引;
②不能跳过索引中的列;
③如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。
到这里可以看到前面的索引列的顺序是多么的重要:这些限制和索引列的顺序有关。在优化性能的时候,可能需要使用相同的列但顺序不相同的索引来满足不同类型的查询需求。
2、哈希索引
哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希吗,哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
在mysql中,只有memory引擎显示的支持哈希索引。这也是memory引擎表的默认索引类型,memory引擎同时也支持B-Tree索引。并且索引自身只需要存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引的速度非常快。然而,哈希索引也有它的限制:
•哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。•哈希索引数据并不是按照索引值的顺序存储的,索引也就无法用于排序。•哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。•哈希索引只支持等值比较查询,包括=、in()、<=>(注意<>和<=>是不同的操作)。也不支持任何范围查询。•访问哈希索引的数据非常快,除非有很多哈希冲突。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。•如果哈希冲突很多的话,一些索引维护操作的代价也会很高。
因为这些限制,哈希索引只适用于某些特定的场合。而一旦适合哈希索引,则他带来的性能提升非常显著。除了memory引擎外,NDB集群引擎也支持唯一哈希索引。InnoDB引擎有一个特殊的功能叫做“自适应哈希索引”,当InnoDB注意到某些索引值被使用的非常频繁时,他会在内存中基于b-tree索引之上再创建一个哈希索引,这样就让b-tree索引也具有哈希索引的一些优点。
3、空间数据索引(R-Tree)
MyISAM表支持空间索引,可以用作地理数据存储。和B-Tree索引不同,这类索引无需前缀查询。空间索引会从所有维度来索引数据。查询时,可以有效的使用任意维度来组合查询。必须使用mysql的GIS相关函数来维护数据。
4、全文索引
全文索引是一种特殊类型的索引,他查找的是文本中的关键词,而不是直接比较索引中的值。全文搜索和其他几类索引的匹配方式完全不一样。他有许多需要注意的细节,如停用词、词干和负数、布尔搜索等。全文索引更类似于搜索引擎的事情,而不是简单的where条件匹配。
在相同的列上同时创建全文索引和基于值的b-tree索引不会有冲突,全文索引适用于match against操作,而不是普通的where条件操作。
二、索引优点
索引可以让服务器快速定位到表的指定位置。但是这并不是索引的唯一作用,到目前为止可以看到,根据索引的数据结构不同,索引也有一些其他的附件作用。
常见的b-tree索引,按照顺序存储数据,所以mysql可以用来做order by和group by操作。因为数据是有序的,所以b-tree也就会将相关的列值都存储在一起。最后,因为索引中存储了实际的列值,所以某些查询只使用索引就能够完成全部查询。据此特性,总结下来索引有如下三个优点:
•索引大大减少了服务器需要扫描的数据量;•索引可以帮助服务器避免排序和临时表;•索引可以将随机I/O变为顺序I/O;
注意: 索引是最好的解决方案吗?
索引并不总是最好的工具。总的来说,只有当索引帮助存储引擎快速查找到记录带来的好处大于其带来的额外工作时,索引才是有效的。对于非常小的表,大部分情况下简单的全表扫描更高效。对于中到大表,索引就非常有效。但对于特大型的表,建立和使用索引的代价将随之增长。这种情况下,则需要一种技术可以直接区分出查询需哟的一组数据,而不是一条记录一条记录的匹配。例如可以使用分区技术。
版权声明:
本文为《大数据真好玩》整理,原作者独家授权。未经原作者允许转载追究侵权责任。
编辑|冷眼丶
微信公众号|大数据真好玩