三、创建高性能索引

目录
[toc]

概述

索引是存储引擎用于快速找到记录的一种数据结构。索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。在数据量小且负载较低时,不恰当的索引对性能可能还不明显,但是当数据量逐渐增大时,性能会急剧下降。索引优化是对查询性能优化最有效的手段。

3.1 索引基础

在Mysql中,存储引擎用类似于书本目录索引的方法,其现在索引中找到对应值,然后根据匹配的索引记录找到对应的数据行。如果在某列上建有索引,通过该列的值查询一条数据,则Mysql将使用该索引找到对应列的行,也就是说,Mysql先在索引上按值进行查找,然后返回所有包含该值得数据行。
索引可以包含一个或多个列的值、如果索引包含多列的值,那么列的顺序也十分重要,因为Mysql只能高效地使用索引的最左前缀。

3.1.1 索引的类型

3.1.1.1 B-Tree索引

当人们谈论索引的时候,如果没有特别指明类型,那么多半说的是B-Tree索引,它使用B-Tree索引数据结构来存储数据。大多数Mysql引擎都支持这种索引。存储引擎以不同的方式使用B-Tree索引,性能也各不相同,各有优劣。例如,MyISAM使用前缀压缩技术使得索引更小,但InnoDB则按照原数据格式进行存储。再如,MyISAM索引通过数据的物理位置引用被索引的行,而InnoDB则根据主键引用被索引的行。

  • 根据索引查找书籍的方式
    B-Tree通常意味着所有的值都是按顺序存储的,并且每个叶子页到根的距离相同。B-Tree索引之所以能快速访问数据,是因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值就可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值得上限和下限。最终存储引擎要么是找到对应的值,要么记录就不存在。

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

  1. 全值匹配 全职匹配指的是和索引中的所有列进行匹配。
  2. 匹配最左前缀 只使用作用的第一列
  3. 匹配列前缀 只匹配某一列值的开头部分
  4. 精确匹配某一列并范围匹配另外一列
  5. 只访问索引的查询 B-Tree通常可以支持“只访问索引的查询”,即查询只需要访问索引,而无须访问数据行。这种指的是"覆盖索引"的优化。

使用B-Tree索引的限制

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

由此可见,索引的顺序非常重要:以上这些限制都和索引的顺序有关。

3.1.1.2 哈希索引

哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码,哈希码是一个较小的值,并且不同键值计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时哈希表中保存指向每个数据行的指针。在mysql中,只有一个Memory引擎显式支持哈希索引。

3.1.1.3 空间数据索引

MyISAM表支持空间索引,可以用作地理数据存储。和B-Tree索引不同,这类索引无须前缀查询。空间索引会从索引维度来索引数据。查询时,可以有效地使用任意维度任意组合查询。但是必须使用Mysql的GIS相关函数如MBRCONTANTS()等来维护数据。

3.1.1.4 全文索引

全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。

总结:
从数据结构角度
1、B+树索引
2、hash索引
3、FULLTEXT索引(InnoDB引擎5.7以后支持)
4、R-Tree索引(用于对GIS数据类型创建SPATIAL索引)
问题:这些索引的区别跟用途在哪?B+树相比hash的优点在哪?
从物理存储角度
1、聚簇索引(clustered index)
2、非聚簇索引(non-clustered index)
问题:实现方式有什么差异?
从逻辑角度
1、主键索引
2、单列索引
3、多列索引
4、唯一索引

3.2.1 为什么要创建索引

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

3.3.1 怎么创建高性能索引

  • 使用独立的列,如果查询中的列不是独立的,则MySQL就不会使用索引,导致索引失效。“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数。
  • 前缀索引和索引选择性。如果需要给很长的字符列建立索引,这会让索引变得大而且慢。这时候就可以用到前缀索引,例如ADD key(column,index) cloumn指的是列的名称,index指的是匹配前缀的位数。前缀索引指的是可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率。但这样也会降低索引的选择性索引的选择性是指,不重复的索引值和数据表的记录总数的比值。索引的选择性越高则查询的效率越高。
    select count(distinct left(city,3))/count(*) as sel from tableName -选择性计算方法
  • 多列索引
    指的是多个列组合起来的索引,而不是多个单独列的索引。事实上,在多个列上建立独立的单列索引大部分情况下并不能提高Mysql的查询性能。这时候不如忽略掉WHERE子句,集中精力优化索引列的顺序,或者创建一个覆盖索引。
  1. 当出现服务器对多个索引做相交操作是(多个AND条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引
  2. 当不需要考虑排序和分组时, 将选择性最高的列放到索引的最前列
  3. 使用最左匹配原则,使用选择性更高的列放到索引前列,可能会比最左匹配的索引性能更好
  • 聚簇索引与非聚簇索引
    聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。

聚簇索引将数据存储与索引放到了叶子节点,找到索引也就找到了数据
非聚簇索引:将数据存储与索引结构分开存放,索引结构的叶子节点存有指向对应数据块的指针,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因

澄清一个概念:innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值由于聚簇索引是将数据跟索引结构放到一块,因此一个表仅有一个
聚簇索引误区:主键自动设置为聚簇索引聚簇索引默认是主键,如果表中没有定义主键,InnoDB 会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB 会隐式定义一个主键来作为聚簇索引。InnoDB 只聚集在同一个页面中的记录。包含相邻健值的页面可能相距甚远。如果你已经设置了主键为聚簇索引,必须先删除主键,然后添加我们想要的聚簇索引,最后恢复设置主键即可。此时其他索引只能被定义为非聚簇索引。这个是最大的误区。有的主键还是无意义的自动增量字段,那样的话Clustered index对效率的帮助,完全被浪费了。刚才说到了,聚簇索引性能最好而且具有唯一性,所以非常珍贵,必须慎重设置。一般要根据这个表最常用的SQL查询方式来进行选择,某个字段作为聚簇索引,或组合聚簇索引,这个要看实际情况。图文解释[图片上传失败...(image-1514e5-1594651284914)]

1.InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。
2.若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(重点在于通过其他键需要建立辅助索引)
3.MyISM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
聚簇索引的优势看上去聚簇索引的效率明显要低于非聚簇索引,因为每次使用辅助索引检索都要经过两次B+树查找,这不是多此一举吗?
聚簇索引的优势在哪
1.由于行数据和叶子节点存储在一起,同一页中会有多条行数据,访问同一数据页不同行记录时,已经把页加载到了Buffer中,再次访问的时候,会在内存中完成访问,不必访问磁盘。这样主键和行数据是一起被载入内存的,找到叶子节点就可以立刻将行数据返回了,如果按照主键Id来组织数据,获得数据更快
2.辅助索引使用主键作为"指针"而不是使用地址值作为指针的好处是,减少了当出现行移动或者数据页分裂时辅助索引的维护工作,使用主键值当作指针会让辅助索引占用更多的空间,换来的好处是InnoDB在移动行时无须更新辅助索引中的这个"指针"。也就是说行的位置(实现中通过16K的Page来定位)会随着数据库里数据的修改而发生变化(前面的B+树节点分裂以及Page的分裂),使用聚簇索引就可以保证不管这个主键B+树的节点如何变化,辅助索引树都不受影响。
3.聚簇索引适合用在排序的场合,非聚簇索引不适合
4.取出一定范围数据的时候,使用用聚簇索引
5.二级索引需要两次索引查找,而不是一次才能取到数据,因为存储引擎第一次需要通过二级索引找到索引的叶子节点,从而找到数据的主键,然后在聚簇索引中用主键再次查找索引,再找到数据
6.可以把相关数据保存在一起。例如实现电子邮箱时,可以根据用户 ID 来聚集数据,这样只需要从磁盘读取少数的数据页就能获取某个用户的全部邮件。如果没有使用聚簇索引,则每封邮件都可能导致一次磁盘 I/O
聚簇索引的劣势
1.维护索引很昂贵,特别是插入新行或者主键被更新导至要分页(page split)的时候。建议在大量插入新行后,选在负载较低的时间段,通过OPTIMIZE TABLE优化表,因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片
2.表因为使用UUId(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫面更慢,所以建议使用int的auto_increment作为主键,主键的值是顺序的,所以 InnoDB 把每一条记录都存储在上一条记录的后面。当达到页的最大填充因子时(InnoDB 默认的最大填充因子是页大小的 15/16,留出部分空间用于以后修改),下一条记录就会写入新的页中。一旦数据按照这种顺序的方式加载,主键页就会近似于被顺序的记录填满(二级索引页可能是不一样的
3.如果主键比较大的话,那辅助索引将会变的更大,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用占用更多的物理空间。为什么主键通常建议使用自增id聚簇索引的数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的。如果主键不是自增id,那么可以想 象,它会干些什么,不断地调整数据的物理地址、分页,当然也有其他一些措施来减少这些操作,但却无法彻底避免。但,如果是自增的,那就简单了,它只需要一 页一页地写,索引结构相对紧凑,磁盘碎片少,效率也高因为MyISAM的主索引并非聚簇索引,那么他的数据的物理地址必然是凌乱的,拿到这些物理地址,按照合适的算法进行I/O读取,于是开始不停的寻道不停的旋转。聚簇索引则只需一次I/O。(强烈的对比)不过,如果涉及到大数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。[图片上传失败...(image-4e2df9-1594651284915)]

  • 覆盖索引
    我们知道在InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次;覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了, 而无需回表查询如普通索引,如果一条SQL需要查询name,name字段正好有索引, 那么直接根据这个索引就可以查到数据,也无需回表。

两个原因导致无法使用覆盖索引,例如以下查询语句

explain select * from products where actor='sean carrey' and title like '%apollo%'

此时结果中 Extra:Using where  只有当 Extra=Using index时才是用来覆盖索引
  1. 没有任何索引能够覆盖这个查询。因某些查询从表中选了所有的列,而没有任何索引覆盖了所有的列
  2. Mysql在索引中执行了LIKE操作。
    解决无法使用覆盖索引的办法:延迟关联
    通过join一个包含所有列为索引(覆盖索引)的子查询表来达成延迟关联,大大提高查询性能。建立索引add key(actor,title,pro_iod)
explain select * from products join (select pro_id from products where actor='sean carrey' and like'%apollo%' ) as t1 on (t1.prod_id=products.pro_id)

但是延迟关联的方法,也受到了数据量大小的影响,当join的结果集很小时,效率越好。

  • 使用索引扫描来做排序

你可能感兴趣的:(三、创建高性能索引)