[关闭]
@lgh-dev 2018-05-04T09:57:54.000000Z 字数 9415 阅读 771

创建高性能索引- 读书笔记

MySQL


前言

索引是存储引擎用于快速找到记录的一种数据结构; 索引能够轻易的将查询性能提高几个数量级,尤其是数据量逐渐变大时.

索引基础

索引可以包含一列或多个列的值,如果包含多个列,那么顺序也很重要,MySQL只能高效的使用索引最左前缀列。创建一个包含两个列的索引,和创建两个只包含一列的索引是大不相同的。
MySQL中索引是在存储引擎层,而不是在服务器层。不同的存储引擎的索引工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。

索引的类型

1、B-Tree索引

索引对如下类型的查询有效

  • 全值匹配: 和索引中的所有列都进行匹配,比如查找姓名为AB,出生日期为C的人
  • 匹配最左前缀: 只使用索引的第一列,比如查找姓为allen的人
  • 匹配列前缀: 只匹配某一列的开头部分,比如查找以J开头的姓的人。
  • 匹配范围值: 比如查询姓在A到C之间的人
  • 精确匹配某一列并范围匹配另外一列: 比如查询所有姓为A,名字是K开头的人。
  • 只访问索引的查询: 查询只需要访问索引,无需访问数据行

因为索引树中的节点是有顺序的,所以除了按值查找,还可以用于查询中的order by操作。如果B-Tree可以按照某种方式查找到值,那就可以按照这种方式进行排序。

B-Tree索引限制

  • 如果不是按照索引的最左列开始查找,则无法使用索引。比如无法查找姓氏以某个字母结尾的人....
  • 不能跳过索引中的列,比如有A,B,C三列索引,不能只查找A,C而跳过B;
  • 如果查询中有某个列的范围查询,则右边所有的列都无法使用索引优化查找;

2、哈希索引

  • 哈希索引基于哈希表实现,只有精确的匹配索引所有列才有效。对于每行数据,存储引擎会对所有的索引列计算一个哈希码,哈希码是个较小的值,不同的健值的行计算的哈希码也不一样。哈希索引把所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
  • 在MySQL中, 只有Memory引擎显式支持哈希索引, 并且支持非唯一的哈希索引。
  • 哈希索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,使得哈希索引查找的速度非常快。

哈希索引的限制

InnoDB引擎有个特殊功能叫做 自适应哈希索引。当InnoDB注意到某些索引值被使用得非常频繁时,会在内存中基于B-Tree索引之上在创建一个哈西索引,这样就让B-Tree索引具有哈希索引的一些优点。
伪哈希索引 它和真正的哈希索引不是一回事,它需要在查询时手动指定使用的哈希函数,而且需要维护哈希值,可以手动维护也可以使用触发器实现;

  • 如果数据表非常大,CRC32()会出现大量的哈希冲突,则可以考虑自己实现一个简单的64位哈希函数;
  • 要避免冲突问题,必须在where条件中带入哈希值和对应的列值。

3、空间数据索引(R-Tree)

  • MyISAM表支持空间索引,用来存储地理数据存储。空间索引会从所有纬度来索引数据;查询时,可以有效的使用任意纬度来组合查询。

4、全文索引

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

5、其他索引类别

  • 分形树索引 ,InnoDB引擎包括聚簇索引,覆盖索引等。

索引的优点

索引可以让服务器快速定位到表的指定位置,但这不是索引的唯一作用,根据创建索引的数据结构不同,索引也有一些其他的附加作用。

索引三大优点

三星系统

索引探讨

高性能的索引策略

1、独立的列

独立的列 是指索引列不能是表达式的一部分,也不能是函数的参数。如果查询中的列不是独立的,那么MySQL就不会使用索引。

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

3、多列索引

索引合并策略

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

5、聚簇索引

  • 聚簇索引不是一种单纯的索引类型,而是一种数据存储方式。当表中有聚簇索引时,它的数据行实际上存放在索引的叶子页中。
  • 聚簇 表示数据航和相邻的键值紧凑的存储在一起。因为无法把数据行同时存储在两个不同的地方,索引一个表只能有一个聚簇索引(覆盖索引可以模拟多个聚簇索引的情况)
  • 存储引擎 负责实现索引,因此不是所有的存储引擎都支持聚簇索引(InnoDB支持),InnoDB将通过主键聚集数据,被索引的列是主键列;如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。
  • 聚簇主键可能对性能有帮助,但也可能导致严重的性能问题。

聚集的数据优点

  • 可以把相关的数据保存在一起。比如电子邮箱,可以根据用户id来聚集数据,只需读取少数的数据页就能获取某个用户的全部邮件。
  • 数据访问更快。聚簇索引将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获取数据比在非聚簇索引中要快.
  • 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。

聚簇索引的缺点

  • 聚簇数据最大限度的提高了I/O密集型应用的性能,但是如果数据全部放在内存,访问的顺序就没有那么重要,聚簇索引优势无法显现了。
  • 插入速度严重依赖于插入顺序。按主键顺序插入InnoDB表中速度最快,如果不是按主键顺序,那么最好用OPTIMIZE TABLE命令重新组织一下表.
  • 更新聚簇索引的代价很高。因为InnoDB强制将每个被更新的行移动到新的位置。
  • 基于聚簇索引的表再插入新行,或者主键被更新导致需要移动行的时候,可能面临页分裂的问题。页分裂会导致表占用更多的磁盘空间.
  • 聚簇索引可能会导致全表扫描变慢,尤其是比较稀疏,或由于 页分裂导致数据存储不连续。
  • 二级索引(非聚簇索引)可能比想象的要更大,因为在二级索引的叶子节点包含了行的主键列。
  • 二级索引访问需要两次索引查找,而非一次。二级索引叶子节点保存的不是行指针,而是行的主键值。

InnoDB和MyISAM的数据分布对比

  • MyISAM按照数据插入的顺序存储在磁盘上。这种分布方式很容易创建索引。
  • MyISAM主键索引和其他索引在结构上没有什么不同。主键索引就是一个名为primary的唯一非空索引。
  • InnoDB的数据分布,在InnoDB总,聚簇索引就是表,不需要独立的行存储。
  • 聚簇索引 的每一个叶子节点都包含了主键值事物ID回滚指针和所有剩余列。如果主键是一个列前缀索引,InnoDB也会包含完整的主键列和剩下的其他列。
  • InnoDB的二级索引和聚簇索引很不相同,二级索引的叶子节点存储的是主键值,而非行指针,这样的策略减少了行移动或页分裂时二级索引的维护工作。
  • InnoDB非叶子节点包含了索引列和一个指向下级节点的指针。

InnoDB中按照主键的顺序插入行

  • 如果是在使用InnoDB表并且没有什么数据需要聚集,那么可以一个和应用无关的主键,最简单的方法是使用AUTO_INCREMENT自增列,保证数据行按顺序写入。
  • 最好避免随机的聚簇索引,特别是对I/O密集型的应用。从性能的角度考虑,使用UUID作为聚簇索引会有很大问题,他会是索引插入完全随机,没有任何聚集特性。
  • UUID主键索引插入不仅时间长,索引占用空间也大。一方面是字段长,另一方面是因为页分裂和碎片导致的。

UUID作为主键索引的问题

  • 写入的目标页不确定,InnoDB再插入之前不得不先并从磁盘找到读取到内存中,导致大量的随机I/O;
  • 写入是乱序的,InnoDB不得不频繁做页分裂操作,导致移动大量的数据,一次插入最少要修改三个页。
  • 频繁的页分裂,页会变得稀疏并被不规则的填充,最终数据碎片话严重。

顺序的主键什么时候会造成更坏的结果?

6、覆盖索引

索引确实是一种查找数据的高效方式,但是MySQL也可以使用索引来直接获取列的数据,这样就不需要读取数据行.如果一个索引包含所有要查询的字段的值,我们就称之为 覆盖索引.

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

  • 索引条目通常远小于数据行大小,索引如果是需要读取索引,那么就会极大的减少数据访问量。这对缓存负载非常重要,因为这种情况响应时间大部分在数据拷贝上。覆盖索引对 I/O 密集型应用也有帮助,因为索引比数据更小,更容易放入内存.
  • 因为索引是按照值顺序存储,索引对于I/O密集型的范围查询会比随机从磁盘读取每一行的I/O要少的多。
  • 一些存储引擎入MyISAM在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用。
  • 由于InnoDB的聚簇索引,覆盖索引对InnoDB表特别有用。InnoDB的二级索引在叶子节点中保存了行的主键值,如果二级主键能覆盖查询,则可以避免对主键索引的二次查询。

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

MySQL两种方式生成有序的结果

  • 如果explain的值的type列的值为index,说明使用了索引扫描排序
  • 按照索引顺序读取数据的速度通常比顺序的全表扫描慢,尤其是在I/O密集型的工作负载中,因为它基本上是随机I/O
  • MySQL可以使用同一个索引,即满足排序,又用于查找行。
  • 只有当索引的顺序和order by子句的顺序完全一致,并且所有列的排序方向都一样,MySQL才能使用索引对结果排序。
  • 如果查询需要关联多张表,只有order by 子句饮用的字段全部是第一个表,才能用索引做排序。
  • order by子句和查找型查询的限制是一样的,需要满足索引最左前缀的要求;否则,MySQL都需要执行排序操作,而无法使用索引排序,
    除非前导列是常量。

8、压缩(前缀压缩)索引

9、冗余和重复索引

MySQL允许在相同的列上创建多个索引,需要单独维护重复的索引,优化器在优化查询时也要逐个考虑,会影响性能。

重复索引 是指在相同的列上按照相同的顺序创建的相同类型的索引。
冗余索引 如果创建了索引(A,B),在创建索引(A)就是冗余索引,因为这只是前一个索引的前缀索引;其他不同类型的索引不会是B-Tree索引的冗余索引。
冗余索引 一般发生在为表添加新索引的时候,还有就是扩展索引(A,ID),ID是主键,对于InnoDB来说主键列已经包含在二级索引中了。

大多数情况都不需要冗余索引,应该尽量扩展已有的索引而非创建索引,有时候扩展已有的索引会导致其变得太大,从而影响其他使用该索引的查询的性能。
表中的索引越多插入的速度就会越慢。一般来说增加新索引会导致insert,delete,update等操作速度变慢。

10、索引和锁

索引案例学习

1、支持多种过滤条件

2、避免多个范围条件

3、优化排序

  1. 例子如下sql所示:
  2. mysql> select <cols> from profiles inner join (
  3. select <primary key cols> from profiles
  4. where x.sex = 'M' order by rating limit 100000,10)
  5. as x using(<primary key cols>);

维护索引和表

1、找到并修复损坏的表

2、更新索引统计信息

MySQL的查询优化器会通过两个API了解存储引擎和索引的分布信息



不同引擎的索引信息

  • InnoDB 引擎通过抽样的方式计算统计信息,先随机读取少量的索引页面,然后以此为样本计算索引的信息,老版本中样本的页数是8,新版本中可以通过参数 innodb_stats_sample_pages来设置样本数量。
  • InnoDB 会在表首次打开,或执行analyze table,或表的大小发生非常大变化 (大小变化超过1/16,或插入20亿行都会触发)的时候计算索引统计信息。
  • InnoDB 在打开某些INFORMATION_SCHEMA表 或使用show table statusshow index,或在客户端开启自动补全功能的时候,都会触发索引统计信息的更新。
  • 只要show index 查看索引统计信息,一定会触发统计信息更新,可以关闭innodb_stats_on_metadata参数。
  • 如果关闭了索引统计信息的自动更新,那么需要周期性的使用analyze table 手动更新。否则,索引统计信息会永远不变,如果数据分布发生大的变化,可能会出现一些狠糟糕的执行问题。

3、减少索引和数据碎片

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

数据存储的碎片化类型

对于MyISAM表,这三类碎片化都可能发生,InnoDB不会出现短小的行碎片,他会移动短小的行重写到一个片段中。

总结

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注