2020 年 6 月 23 日 创建高性能的索引

用户头像
瑞克与莫迪
关注
发布于: 2020 年 06 月 22 日

索引在存储引擎用于快速找到记录的一种数据结构,这是索引的基本功能,索引优化是对查询性能最有效的手段了,索引能够轻易将查询性能提高几个数量级。

1、索引的基础

索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为MySQL只能高效地使用索引的最左前缀列。

1、索引的类型

在MySQL中,索引是在存储引擎层而不是服务层实现的,所以并没有统一的标准:不同的存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。

MySQL支持的索引类型有:B-Tree索引,哈希索引,空间数据索引,全文索引,聚簇索引,覆盖索引等

B-Tree索引

当人们没有谈论索引没有指定类型的时候,多半说的是B-Tree索引,它使用B-Tree数据结构来存储数据。

存储引擎以不同的方式使用B-Tree索引,性能也各有不同,各有优劣。

B-Tree索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始搜索。

根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层节点。

B-Tree对索引列是顺序组织存储的,所以非常适合查找范围数据。

注意:索引对多个值进行排序的依据是Create Table语句中定义索引时列的顺序

可以使用B-Tree索引的查询类型:

1、全职匹配

2、匹配最左前缀

3、匹配范围值

4、精确匹配某一列并范围匹配另外一列

5、只访问索引的查询

一般来说除了按值查找之外,索引还可以用于查询中的Order By操作(按顺序查找)。

使用B-Tree索引的限制

1、如果不是按照索引的最左列开始查找,则无法使用索引

2、不能跳过索引中的列

3、如果查询中有某个列的范围查询,则其右边所有的列都无法使用索引优化查找。

在性能优化的时候,可能需要使用相同的列但顺序不同的索引来满足不同的类型的查询需求。

哈希索引

定义:哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码,哈希码是一个较小的值,并且不同的键值的行计算出来的哈希码也不一样。

哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。

哈希索引的限制

1、哈希索引只包含哈希值和行指针,而不是存储字段值,所以不能使用索引中的值避免读取行。

2、哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。

3、哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。

4、哈希索引只支持等值比较查询,包括= 、IN等。也不支持任何范围查询

5、当出现哈希冲突时,存储引擎必须遍历链表中所有的行指针,逐行进行比较

6、如果哈希冲突很多的话,一些索引维护操作的代价也会很高

空间数据索引

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

全文索引

全文索引是一种特殊的索引,它查找的是文本中的关键词,而不是直接比较索引中的值,全文索引适用于MATCH AGAINST 操作,而不是普通的Where条件操作。

2、索引的优点

1、索引可以让服务器快速地定位到表的指定位置。

2、最常见的B-Tree索引按照顺序存储数据,所以MySQL可以用来做Order By 和GROUP BY操作

3、索引大大减少了服务器需要扫描的数据量

4、索引可以帮助服务器避免排序和临时表

5、索引可以将随机I/O变成顺序I/O

3、高性能的索引策略

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

1、独立的索引

“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数。

2、前缀索引和索引选择性

有时候需要索引很长的字符列,这会让索引变大且慢,一个策略是前面提到过的模拟哈希索引。

通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率,但这样也会降低索引的选择性。索引的选择性是指,不重复的索引值和数据表的记录总数的比值。

索引的选择性越高则查询效率越高。

给city列添加前缀索引

mysql> Alter table demo add key(city(7));

前缀索引是一种能使索引更小,更快的有效办法。但另一方面也有缺点:MySQL无法使用前缀索引做Order By和Group by ,也无法使用前缀索引做覆盖扫描

3、多列索引

多列索引中一个常见的错误:为每个列创建独立的索引,或者按照错误的顺序创建多列索引。

在多个列上建立独立的单列索引发部分情况下并不能提高MySQL的查询性能,MySQL引入了一种“索引合并”的策略,一定程度上可以使用表上的多个单列索引来定位指定的行。

在MySQL5.0版本以后,查询能够同时使用这两个单列索引进行扫描,并将结果进行合并。这种算法有三个变种:OR条件的联合,AND条件的相交,组合前两种情况的相交与联合。

联合索引有时候是一种优化结果,但实际上更多时候说明了表上的索引建的很糟糕:

1、当出现服务器对多个索引相交操作时,通常意味着需要一个包含所有相关列的多列索引

2、当服务器需要对多个索引联合操作时,通常需要耗费大量的CPU和内存资源在算法的缓存、排序和合并操作上。

4、选择合适的索引列顺序

正确的顺序依赖于使用索引的查询,并且同时需要考虑如何更好的满足排序和分组要求。

在一个多列B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,等等,所以索引可以按照升序或者降序进行扫描,以满足精确符合列顺序的Order by ,Group By 和Distinct等子句的查询需求。

当不需要考虑排序和分组时,将选择性最高的列(比较有区分度)放在前面通常是很好的,这时候索引的作用只是作用于优化Where条件的查找。

5、聚簇索引

聚簇索引病史一种单独的索引类型,而是一种数据的存储方式。具体的细节依赖于实现方式,但Innodb的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。

当表有聚簇索引时,它的数据行实际上存放在索引的子页中。

术语“聚簇”表示数据行和相邻的键值紧凑的存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。

如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。

InnoDB只聚集在同一个页面的中记录,包含相邻键值的页面可能会相距很远

聚集的数据有一些重要的优点:

1、可以把相关的数据保存在一起

2、数据访问速度更快

3、使用覆盖索引扫描的查询可以直接使用页节点中的主键值

聚簇索引的缺点;

1、聚簇索引最大限度地提高了I/O密集型应用性能,如果数据全部都存放在内存中,则访问的顺序就没那么重要了。

2、插入速度严重依赖于插入顺序

3、根据聚簇索引列的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置

4、基于聚簇索引的表在插入新行,或者主键被更新导致需要移动的时候,可能面临“页分裂”的问题

6、覆盖索引

如果一个索引包含所有需要查询的字段的值,我们就称之为“覆盖索引”。

覆盖索引是非常有用的工具,能够极大的提高性能,覆盖索引的好处:

1、索引条目通常远小于数据行大小,所以如果只需要读取索引 ,那MySQL就会极大地减少数据访问量。

2、因为索引是按照列值顺序存储的,所以对于I/O密集型的范围查询会比随机从磁盘读取每一行数据的I/O要少得多。

3、由于InnoDB的聚簇索引,覆盖索引对InnoDB表特别有用。InnoDB的二级索引在叶子节点中保存了行的主键值,所以如果二级主键能够覆盖查询,则可以避免对主键索引的二次查询

覆盖索引必须要存储索引列的值,而哈希索引,空间索引和全文索引等都不存储索引列的值,所以MySQL只能用B-Tree索引做覆盖索引

7、使用索引扫描来做排序

MySQL有两种方式可以生成有序的结果:通过排序操作;或者按索引顺序扫描;如果explain出来的type列的值为“index”,则说明MySQL使用了索引扫描来做排序。

注意

1、只有当索引的列顺序和Order by子句的顺序完全一致,并且所有的列的排列方向都一样时,MySQL才能够使用索引来对结果做排序。如果查询需要关联多张表,则只有当Order by子句的引用的字段全部为第一个表时,才能使用索引做排序。

2、order by 子句和查找类型查询的限制是一样的:需要满足索引的最左前缀的要求,否则MySQL都需要执行排序操作,而无法利用索引排序

8、压缩(前缀)索引

(了解即可 )

MyISAM使用前缀压缩来减少索引的大小,从而让更多的索引可以放入内存中,这在某些情况下能极大地提高性能

压缩块使用更少的空间,代价是某些操作更慢。因为每个值的压缩前缀都依赖前面的值,所以MyISAM查找时无法在索引块使用二分查找而只能从头开始扫描。

9、冗余和重复索引

MySQL允许在相同的列上创建创建多个索引,无论是有意的还是无意的,MySQL需要单独维护重复的索引,并且优化器在优化查询的同时,也需要逐个进行考虑,这会影响性能。

重复索引是指在相同的列上按照相同的顺序创建相同类型的索引。应该避免创建重复索引,发现以后也应该立即删除

冗余索引和重复索引有一些不同,如果创建了索引(A,B),再创建索引(A)就是冗余索引。因为这只是前一个索引的前缀索引。但是如果创建索引(B,A)则不是冗余索引。

大多数情况下都不需要冗余索引,应该尽量扩展已有的索引而不是创建新索引。

10、索引和锁

索引可以查询锁定更少的行,InnoDB只有在访问行的时候才会对其加锁,而索引能够减少InnoDB访问的行数,从而减少锁的数量。

InnoDB在二级索引上使用共享(读)锁,但访问主键索引需要排他锁。这消除了使用覆盖索引的可能性。

4、维护索引和表

维护表有三个主要的目的:找到并修复损坏的表,维护准确的索引统计信息,减少碎片。

1、找到并修复损坏的表

对于MyISAM存储引擎,表损坏通常是系统崩溃导致的。其他的引擎也会由于硬件问题、或者操作系统的问题导致索引损坏。

损坏的索引会导致查询返回错误的结果或者莫须有的主键冲突等问题,严重时甚至还会导致数据库的崩溃。可以尝试运行CHECk TABLE 来检查是否发生了表损坏,CHECK TABLE通常能够找出大多数的表和索引的错误。

可以使用REPAIR TABLE来修复损坏的表,但同样不是所有的存储引擎都支持该命令。如果存储引擎不支持,也可以通过一个不做任何操作的ALTER操作来重建表。

2、更新索引统计信息

3、减少索引和统计碎片

B-Tree索引可能会碎片化,这会降低查询的效率。碎片化的索引可能会以很差或者无序的方式存储在磁盘上。

表的数据存储也可能碎片化,然而数据存储的碎片化比索引更加复杂。有三种类型的数据碎片:

1、行碎片

这种碎片指的是数据行被存储为多个地方的多个片段中

2、行间碎片

行间碎片是指逻辑上顺序的页,或者行在磁盘上不是顺序存储的。行间碎片对诸如全表扫描和聚簇索引扫描之类的操作是有很大的影响

3、剩余空间碎片

剩余空间碎片是指数据页中有大量的空余空间

处理方式:

可以通过执行OPTIMIZE TABLE或者导出再导入的方式来重新整理数据

6、总结

在MySQL中,大多数情况下都会使用B-Tree索引,其他类型的索引大多只适用于特殊的目的,如果在合适的场景中使用索引,将大大提高查询的响应时间。

在选择索引和利用索引做查询时,应该记住以下三个原则:

1、单行访问是很慢的。如果服务器从存储中读取一个数据块只为了获取其中的一行,那么就浪费了很多工作。最好读取的块中能包含尽可能多所需要的行,使用索引可以创建位置引用以提升效率

2、按顺序访问范围数据很快的,这有两个原因。第一:顺序I/O不需要多次磁盘寻道。第二:如果服务器能够按照顺序读取数据,那么就不在需要额外的排序 操作。

3、索引覆盖查询是很快的

发布于: 2020 年 06 月 22 日 阅读数: 37
用户头像

瑞克与莫迪

关注

还未添加个人签名 2020.05.18 加入

还未添加个人简介

评论

发布
暂无评论
2020年6月23日       创建高性能的索引