创建高性能索引

时间:2021-07-20 19:04:54

索引是存储引擎用于快速查找记录的一种数据结构。索引对于良好的性能非常关键。尤其是当表中的数据量越来越大的时候,索引对性能的影响愈发重要,在数据量较小并且负载较低的时候,不恰当的索引对性能的影响不是很明显,但是随着数据量的增加,性能会急剧下降。

索引优化是对查询性能优化最有效的手段,索引能够轻易将查询性能提高几个数量级,“最优”的索引有时候比一个“好的”索引在性能上要好两个数量级。然而,创建一个真正“最优”的索引经常需要重写查询。

在MySQL中,一次查询过程中,首先在索引中找到对应的值,然后根据匹配的索引记录找到对应的数据行。索引可以包含多个列的值,如果索引包含多个列,那么列的顺序也十分重要,因为MySQL只能高效地使用索引的最左前缀列

索引的类型

索引有很多类型,可以为不同场景提供更好的性能,在MySQL中,索引是在存储引擎层而不是服务器层实现的。所以,并没有统一的索引标准:不同的存储引擎的工作方式不一样,也不是所有的存储引擎都支持所有类型的索引,即使多个存储引擎支持同一种类型的索引,其底层实现也不可能相同。

B-TREE索引

如果没有特别指明索引类型,默认索引类型为B-TREE索引。它通过B-TREE数据结构来进行存储数据,大多数MySQL引擎都支持这种索引。存储引擎以不同的方式使用B-TREE索引,性能也有所不同,各有优劣。MyISAM使用前缀压缩技术使得索引更小,但InnoDB则按照原数据格式进行存储。MyISAM索引通过数据的物理位置引用被索引的行,而InnoDB则根据主键引用被索引的行。

B-TREE索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。B-TREE对索引列是顺序组织存储的,所以很适合查找范围数据。B-TREE索引适用于全键值、简直范围或者键前缀查找,其中键前缀查找只适用于根据最左前缀查找

但是B-TREE索引的限制:

  • 如果不是按照索引的最左列开始查找,则无法使用索引。
  • 不能跳过索引中的列
  • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引最优化查找。

索引列的顺序非常重要:这些限制都和索引列的顺序有关。

哈希索引

哈希索引是基于哈希表来实现的,只有精确匹配索引所有列的查询才有效。在MySQL中,只有Memory引擎显式支持哈希索引,也是Memory引擎表默认索引类型,Memory引擎同时支持B-TREE索引。而且Memory引擎是支持非唯一索引的。

因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引查找的速度非常快,但是也有其限制:

  • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。
  • 哈希索引数据并不是按照索引顺序存储的,所以无法用于排序。
  • 哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。
  • 哈希索引只支持等值比较查询。
  • 访问哈希索引的数据非常快,除非有很多哈希冲突
  • 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。

除了Memory引擎以外,NDB集群引擎也支持唯一哈希索引,并且在NDB集群引擎中作用非常特殊。

InnoDB引擎有一个特殊功能:“自适应哈希索引”。InnoDB会注意到某些索引值被使用得非常频繁时,它会在内存中基于B-TREE索引之上再创建一个哈希索引。

空间数据索引

MyISAM表支持空间索引,可以用作地理数据存储。和B-TREE索引不同,这类索引无需前缀查询。空间索引会从所有维度来索引数据。查询时,可以有效地使用任意维度来组合查询。

全文索引

全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。全文搜索和其他几类索引的匹配方式完全不同,它有许多需要注意的细节,如停用词、词干、布尔搜素等等。全文索引更类似于搜索引擎做的事情,而不是简单的Where条件匹配。

索引的优点

  1. 索引大大减少了服务器需要扫描的数据量
  2. 所以可以帮助服务器避免排序和临时表
  3. 索引可以将随机I/O变为顺序I/O

Relational Database Index Design and the Optimizers一书中,评价索引是否适合某个查询的“三星系统”:

  • 索引将相关的记录放到一起则获得一星
  • 如果索引中的数据顺序和查找中的排列顺序一致则获得二星
  • 如果索引中的列包含了查询中需要的全部列则获得三星。

高性能的索引策略

正确地创建和使用索引是实现高性能查询的基础。

  • 独立的列 我们通常会看到一些查询不当地使用索引,或者使得MySQL无法使用已有的索引。如果查询中的列不是独立的,则MySQL就不会使用索引。“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数。
  • 前缀索引和索引的选择性 有时候需要索引很长的字符列,这会让索引变得大且慢。一个策略是前面提到过的模拟哈希索引。通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率,但是也会降低索引的选择性。(索引选择性是指不重复的索引值和数据表的记录总数的比值)索引的选择性越高则查询效率越高。
  • 多列索引 一个常见的错误是:为每个列创建独立的索引,或者按照错误的顺序创建索引。但实际上,在多个列上建立独立的单列索引大部分情况下并不能提高MySQL的查询性能。5.0和之后的版本引入“索引合并”的策略,一定程度上缓解了这个问题。(但没有彻底解决)
    索引合并策略有时候是一种优化的结果,但实际上更多时候则说明了表上的索引很糟糕
  1. 当出现服务器对多个索引做相交操作的时候,意味着需要一个包含所有相关列的多列索引而不是多个独立的单列索引
  2. 当服务器需要对多个索引做联合操作时,通常需要耗费大量的CPU和内存资源在算法上的缓存、排序和合并。
  3. 优化其不会把这些计算到“查询成本”中,优化器只关心随机页面的读取。这会使得查询的成本被“低估”,导致该执行计划还不如直接走全表扫描。
  • 选择合适的索引列顺序。 正确的顺序依赖于使用该索引的查询,并且同时需要考虑如何更好地满足排序和分组的需要。 在一个多列B-TREE中,索引列的顺序意味着索引首先要按照最左列进行排序,其次是第二列,以此类推。

对于如何建立索引列的顺序有一个经验法则:将选择性最高的列放到索引最前面。