# 《MySQL技术内幕-InnoDB存储引擎》整理4-索引与算法

一、InnoDB存储引擎索引概述

InnoDB存储引擎支持以下几种常见的索引:①B+树索引;②全文索引;③哈希索引

InnoDB存储引擎支持的哈希索引是自适应的,InnoDB存储引擎会根据表的使用情况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引。B+树索引就是传统意义上的索引,B+树索引的构造类似于二叉树,根据键值快速找到数据(B不是代表二叉二而是代表平衡),并且B+树索引找到的只是被查找数据行所在的页,然后将页读到内存中,在内存中进行查找直至找到数据

二、数据结构与算法

1、二分查找法

基本思想是:将记录按有序化排列,在查找过程中采用跳跃式方法查找,即先以有序数列中的中间位置为比较对象,如果要查找的元素值小于该中点元素,则将带查找序列缩小为左半部分,否则为右半部分

2、二叉查找树和平衡二叉树(AVL树)

在二叉查找树中,左子树的键值总是小于根的键值,右子树的键值总是大于根的键值,可以通过中序遍历得到键值的排序输出。但若想最大性能的构造出一颗二叉查找树,需要这颗二叉查找树是平衡的。

平衡二叉树的定义:首先符合二叉查找树的定义,其次满足任何节点的两个子树的高度最大差为1。平衡二叉树的查找性能较高,但不是最好的,最好性能的查找树为最优二叉树。

3、B+树

B+树由B树和索引顺序访问方法演化而来,简单来说,B+树是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在B+树中,所有记录节点都是按照键值的大小顺序放在同一层的叶子节点上,由各叶子节点指针进行连接

1、B+树的插入操作

B+树的插入操作必须保证插入后叶子节点中的记录依然排序,同时需要考虑插入到B+树的三种情况导致的不同插入算法:

  • 叶子页仍有空间,索引页仍有空间的情况下,直接将记录插入到叶子节点
  • 叶子页没有空间,索引页仍有空间的情况下,拆分叶子页,将中间的节点放入索引页中,小于中间节点的记录方左边,大于等于中间节点的记录方右边
  • 叶子页没有空间,索引页没有空间的情况下,拆分叶子页,小于中间节点的记录放左边,大于等于中间节点的记录放右边,拆分索引页,小于中间节点的记录放左边,大于中间节点的记录放右边,中间节点放入上一层Index Page

此外为了保持平衡对于新插入的键值可能需要做大量的拆分页的操作,B+树提供了类似平衡二叉树的旋转功能。当叶子页已满,但是其左右兄弟节点未满,会将记录转移到兄弟节点上

2、B+树的删除操作

B+树使用填充因子来控制树的删除变化,50%是可设定的最小值,B+树的删除操作同样必须保证删除后叶子节中的记录依然排序,B+树的删除操作需要考虑三种情况,同时结合填充因子的变化来衡量:

  • 叶子节点大于等于填充因子,且中间节点小于填充因子时,直接将记录从叶子节点删除,如果该节点是索引页的节点,用该节点的右节点代替
  • 叶子节点小于填充因子,且中间节点小于填充因子时,合并叶子节点和它的兄弟节点,同时更新索引页
  • 叶子节点大于等于填充因子,且中间节点大于等于填充因子时,合并叶子节点和它的兄弟节点,更新索引页,合并索引叶和它的兄弟节点

4、B+树索引

B+树索引的本质就是B+树在数据库中的实现,B+索引在数据库中的特点是高扇出性,树的高度一般在2~4层。数据库中的B+树索引分为聚集索引和辅助索引,其区别是叶子节点存放的是否是一整行的信息

1、聚集索引

聚集索引是按照每张表的主键构造的一颗B+树,同时叶子节点存放的即为整张表的行数据,也称为数据页,另外索引组织表中的数据也是索引的一部分。聚集索引非物理上连续,而是逻辑上连续,它们通过双向链表链接,页按照主键的顺序排序。

2、辅助索引

对于辅助索引,叶子节点不包含行记录的全部数据,叶子节点除了包含键值书签,每个叶子节点中的索引行还包含了一个书签,该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。由于InnoDB存储引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。

3、B+树索引的分裂

B+树索引页的分裂并不总是从页的中间记录开始,这样会导致空间的浪费。InnoDB存储引擎的Page Header中来保存插入的Page_Last_Insert、Page_Direction、Page_N_Direction来保存顺序信息,并通过它们来决定是向左分裂还是向右分裂,同时决定分裂点记录为哪一个

4、B+树索引的管理

1、索引管理

索引的创建和删除可以通过两种方法,一种是Alter Table,另一种是Create/Drop Index。用户可以对整个列的数据进行索引,也可以只索引列开头的部分数据,可以通过Show Index From xxx命令可以查看索引的信息。

2、Fast Index Creation

Fast Index Creation即快速索引创建,对于辅助索引的创建,InnoDB存储引擎会对创建索引的表加上一个S锁,可以并发地读事务。对于辅助索引的删除,InnoDB存储引擎只需要更新内部视图,并将辅助索引的空间标记为可用,同时删除MySql数据库内部视图上对该表的索引定义。

3、Online Schema Change

Online Schema Change即在线架构改变,在线是指在事务的创建过程中,可以有读写事务对表进行操作,以提高原有Mysql数据库在DDL操作时的并发性。

4、Online DDL

Mysql5.6开始支持Online DDL操作,允许辅助索引创建的同时,还允许DML操作,极大的提高了Mysql数据库在生产环境中的可用性。此外可以使用Alter Table语法选择索引的创建方式:

  • Copy表示按照创建临时表的方式创建索引
  • Inplace表示索引创建或删除操作不需要创建临时表
  • Default表示根据old_alter_table来判断是通过Inplace还是Copy,该参数默认未Off,表示采用Inplace方式

创建索引中的Lock部分为索引创建或删除时对表添加锁的情况:

  • None表示执行索引创建或删除操作时,对目标表不添加任何锁,即事务可以进行读写操作,不会收到阻塞,支持最大的并发度;
  • Share表示执行索引的创建过删除操作时,对目标表加一个S锁,支持并发地读事务,遇到写事务会等待
  • Exclusive表示执行索引创建或删除操作时,对目标表加上一个X锁,读写事务都不能进行,因此会阻塞所有的线程
  • Default表示首先会判断当前操作是否可以使用None模式,若不能则判断是否可以使用Share模式,最后判断是否可以使用Exclusive模式

5、Cardinality值

1、什么是Cardinality

不是所有查询条件中出现的列都需要添加索引,当某个字段的取值范围很广,机会没有重复时即属于高选择性,此时使用B+树索引是最合适的,如何查看索引是否是高选择性的,通过Show Index结果中的Cardinality列可以知晓,它表示所重复记录数量的预估值,理论情况下它与行数据的比例应该尽可能的接近1

2、InnoDB存储引擎的Cardinality统计

数据库对于Cardinality的统计是在引擎层进行的,通过采样方式完成。其统计发生在两个操作中,Insert和Update,当表中1/16的数据发生变化后会进行更新,或者对表中某一行的数据频繁地进行更新操作时,即当更新统计参数stat_modified_counter大于2000000000时,同样会更新Cardinality信息

6、B+树索引的使用

1、不同应用中B+树索引的使用

对于OLTP应用,即查询一小部分数据时,对索引的使用则是通过索引取得表中少部分的数据。对于OLAP应用,较为复杂的查询涉及表的关联查询,此时索引的添加依旧是有意义的

2、联合索引

联合索引是指对表上多个列进行索引,联合索引也是一颗B+树,不同的是联合索引的键值的数量不是1而是大于等于2的。对于(a,b)类型的联合索引,对于单个的a列查询同样可以使用该联合索引。此外它的另一个好处是已经对第二个键值进行了排序处理。

3、覆盖索引

InnoDB存储引擎支持覆盖索引,即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录,此外覆盖索引的好处是辅助索引不包含整行记录的所有信息,其大小远小于聚集索引,可以减少大量的IO操作。此外针对一些统计查询,也可以减少部分IO操作

4、优化器选择不使用索引的情况

在某些情况下,优化器会选择扫描聚集索引查找数据,即全表扫描,这类情况多发生于范围查找和Join链接操作。当访问数据占整个表的20%时,优化器会选择聚集索引来查找数据

5、索引提示

Mysql数据库支持显示地告诉优化器使用哪个索引,以下两种情况下需要使用索引提示(Index Hint):

  • Mysql数据库优化器错误地选择了某个索引,导致SQL执行很慢
  • 某SQL语句可以选择的索引非常多,优化器执行计划时间的开销大于SQL语句本身

6、Multi-Range Read优化

Mysql5.6版本开始支持Multi-Range Read(MRR)优化,其目的是为了减少磁盘的随机访问,并且将随机访问转换为较为顺序的数据访问,对于IO-bound类型的SQL查询语句带来极大的提升,此外可以并减少缓冲池中页被代替的次数,并批量处理对键值的查询操作。

对于InnoDB和MyISAM存储引擎的范围查询和Join查询操作,MRR的工作方式如下:

  • 将查询得到的辅助索引值存放于一个缓存中,且缓存中的数据是根据辅助索引键值排序的
  • 将缓存中的键值根据RowID进行排序
  • 根据RowID的排序顺序来访问实际的数据文件

此外启用Multi-Range Read还可以将某些范围的查询拆分为键值对,来进行批量的查询,而非查询出来一部分后再进行过滤

7、Index Confition Pushdown优化

一般情况下在进行索引查询时,会更加索引来查找数据,然后再根据Where条件来过滤记录,而在Mysql5.6支持Index Confition Pushdown以后,Mysql数据库会在去除索引的同时,判断是否可以进行Where条件的过滤,在某些情况下,可以大大减少上层SQL层对记录的fetch,从而提高性能

7、哈希算法

1、哈希表

哈希表也称散列表,由直接寻址表改进而来,在该方式下可以根据关键字计算出槽的位置,再利用哈希函数h可以将关键字域银蛇到哈希表的槽位上。

对于哈希碰撞问题,数据库一般采用的技术称为链接法。链接法会将散列到同一槽中的所有元素放在一个链表中。对于哈希函数,数据库一般采用除法散列实现,如将关键字映射到m个槽的某一个去,则通过k mod m得到余数

2、InnoDB存储引擎中的哈希算法

InnoDB存储引擎中采用哈希算法对字典进行查找,其冲突机制采用链表方式,哈希函数采用除法散列方式。m的取值为略大于2倍的缓冲池页数量的质数。

3、自适应哈希索引

自适应哈希索引由数据库自身创建并使用,经哈希函数映射到一个哈希表中,对于字典的查找非常快速,但是对于范围查找无能为力

8、全文检索

1、概述

全文检索是将存储于数据库中的整本书或者整篇文章中的任意内容信息查找出来的技术,它可以根据需要获得全文中所有的有关章、节、段、句、词等信息,也可以进行各种统计和分析

2、倒排索引

全文索引通常使用倒排索引实现,倒排索引和B+树索引一样也是一种索引结构,它在辅助表中存储了单词与单词自身在一个或多个文档中所在位置之间的映射。其通常利用关联数组实现,拥有两种表现形式:

  • inverted file index,表现形式为{单词,单词所在文档的ID}
  • full inverted index,其表现形式为{单词,(单词所在文档的ID,在具体文档中的位置)}

3、InnoDB全文检索

InnoDB存储引擎从1.2.x版本开始支持全文检索的技术,采用full inverted index方式,(DocumentId,Postion)被视为一个ilist,因此在全文检索的表中,有两个列,一个是Word字段,另一个是ilist字段,Word字段上设有索引。

倒排索引需要将word存放在一张表中,这个表称为辅助表,InnoDB共有6个辅助表,并且持久化地存放在磁盘上,此外InnoDB还用到了FTS Index Cache全文检索索引缓存,用来提高全文检索的性能。它是一个红黑树结构,根据(word,ilist)进行排序。InnoDB会批量对辅助表进行更新,而不是每次插入后更新一次辅助表。

当前InnoDB存储引擎的全文索引还存在以下限制:

  • 每张表只能有一个全文检索的索引
  • 由多列组合而成的全文检索的索引列必须使用相同的字符集与排列规则
  • 不支持没有单词界定符的语言,如中文、日语、韩语

4、全文检索

Mysql支持全文检索的查询,其语法为Mactch()...Against()语法支持全文检索的查询,Match指定了需要被查询的列,Against指定了使用何种方法去进行查询,下面介绍查询模式

1、Natural Language

全文检索通过Match函数进行查询,默认采用Natural Language模式,其表示查询带有指定word的文档,如通常使用的select * from tableA where body like ‘%xx%‘,若使用全文检索技术,可以使用select * from tableA where Match(body) Against(‘xx‘)

Where条件中的使用Match函数,查询返回的结果是根据相关性进行排序的,相关性最高的结果放在第一位,其计算依据如下:1).word是否在文档中出现;2).word在文档中出现的次数;3).word在索引中列的数量;4).多少个文档包含该word

2、Boolean

Mysql数据库允许使用In Boolean Mode修饰符来进行全文的检索,Boolean全文检索支持以下几种操作符:

  • +表示该word必须存在
  • -表示该word必须被排除
  • (no operator)表示该word是否是可选的,如果出现,相关性会更高
  • @distance表示查询的多个单词之间的距离是否在distance之内,其单位是字节
  • >表示出现该单词时增加相关性
  • <表示出现该单词时降低相关性
  • ~表示允许出现该单词,但是出现时相关性为负
  • *表示以该单词开头的单词
  • ‘‘表示短语
3、Query Expansion

Mysql数据库支持全文检索的扩展查询,此类查询在查询的关键词太短,用户需要隐含知识时进行。通过在查询短语中添加With Query Expansion或者In Natural Language Mode With Query Expansion可以开启blind query expansion。该查询分为两个阶段:

  • 第一阶段:根据搜索的单词进行全文索引查询
  • 第二阶段:根据第一阶段产生的分词再进行一次全文检索的查询

# 《MySQL技术内幕-InnoDB存储引擎》整理4-索引与算法

上一篇:数据库(单表查询,多表查询,子查询)


下一篇:理解数据库的事物,ACID,cap