4. InnoDB的索引与算法

专栏地址:

MySQL系列文章专栏



1. 常见的索引模型

索引的目的是为了提高数据的查询检索效率,常见的索引模型有:哈希表、有序数组、搜索树

哈希表

哈希表以一种 key-value 的形式存储数据。由于存储结构不是有序的,所以哈希索引不适合做范围查询、索引完成排序等,但其在等值查询的场景下具有绝对优势。

在Redis的ZSET中,在哈希表的基础上增加了跳表结构,使其既可以支持哈希访问,也可以做快速的范围查询。

有序数组

有序数组在等值查询和范围查询的场景下性能都很优秀,时间复杂度达到了O(lgN)。但是其数据增加和删除的成本太高,只实用于静态存储引擎。

搜索树

二叉树的特点是:每个父节点,比其左儿子大,比其右儿子小。其查询的时间复杂度是O(lgN)。为了维持O(lgN)的查询复杂度,就得保持这棵树是平衡二叉树,所以更新操作也是O(lgN)。

搜索树可以有多叉,子节点之间保持从左到右的有序递增。

二叉树的搜索效率最高,但在数据库索引很少使用,原因是索引不仅存在于内存,还要写到磁盘上。为了减少IO次数,就必须让查询过程中访问尽量少的数据块。

2. B+树索引

B+Tree是为了硬盘或者其它外存设计的平衡多路查找树,与平衡二叉查找树相比,B+Tree是多叉树,每个节点保存多个元素,高度更低,可有效减少IO次数

MyISAM引擎和InnoDB都是用B+Tree作为索引结构,不同的是,MyISAM索引和数据是分离的,B+树中保存着数据的地址;InnoDB中,表数据就是按照B+Tree组织,叶子节点存放着数据记录,这种表结构称之为索引组织表。在数据库中,B+Tree的高度一般在2-4层,查询操作只需2-4次IO。

2.1 B-Tree–有序数组+平衡多叉树

  1. 一个m阶的B+Tree每个节点最多m个子节点以及m-1个键。(PS:m-1个键划分出m个区间,子节点不在出现父结点已有的元素,每个节点元素只出现一次
  2. 除了根节点和叶子节点外,每个节点至少有m/2个子节点。
  3. 每个节点的元素从小到大排序。

在这里插入图片描述

2.2 B+Tree—有序数组+链表+平衡多叉树

  1. 节点元素与其子节点数相同。(m阶,m个元素,m个子节点)
  2. 父节点保存的是每个子树的最小值
  3. 由2可知,叶子节点保存所有元素
  4. 叶子节点通过指针(双向链表)相连

在这里插入图片描述

在这里插入图片描述

2.3 对比

B-Tree中每个元素只出现一遍,而B+Tree中所有元素都在叶子节点出现。所以B-Tree需要在每个节点中保存数据,而B+Tree只需在叶子节点保存数据即可,故B+Tree的内部结构更小,阶数可以更大。B+Tree除了随机检索还支持顺序检索。B+Tree的查询效率更加稳定,任何元素的查找都需要从根节点到叶节点,路径长度一致。

2.4 B+树的操作

B+树为了保证索引的有序性,在索引发生变更的时候需要进行必要的维护。

查找

类似二叉树查找,在节点内部使用二分查找。

增加

索引的随机插入,如果当前数据页已经满了,则会导致页分裂——申请一个新的数据页,然后移动部分数据到新的数据页。页分裂会到了性能损耗(涉及到磁盘操作),也降低了页的使用率。InnoDB为了减少页分裂,在兄弟节点没有满的情况下,会进行旋转操作,将记录转移到兄弟节点上。

追加不会导致页分裂,但和随机插入一样,将节点插入父节点会使B+树向上生长——父节点数量也都超出,B+树变高。

删除

相邻两个页由于删除导致利用率较低后,会对数据页进行合并。InnoDB使用填充因子来控制树的删除变化。

2.5 InnoDB为什么选择B+树

B+Tree是为需要外存随机访问设计的平衡多路查找树。

与二叉树相比,其每个节点可以保存的元素更多,每次IO可以读取到的元素更多;树的高度也因而更低,所需的IO次数更少。

与B-Tree相比,B+Tree只在叶子节点保存数据,内部结构更加紧凑,阶数更大,因而IO次数也更少;B+树查询效率更加稳定,还支持顺序检索。

但是,对于离根节点较近的子节点,B-Tree由于子节点同时包含了Key以及Value(数据地址),因此无需访问至叶子节点,速度更快。

3. 索引组织表

InnoDB中的表数据是一颗按照主键构建的B+树,这种存储方式称之为索引组织表,主键索引树的叶子节点保存了完整的数据记录。

3.1 聚簇索引

聚簇索引(clustered index)定义了表中数据的存储结构,在InnoDB中主键索引也被称为聚簇索引。

InnoDB要求表必须有主键(MyISAM可以没有)。如果没有,InnoDB会自动选择一个可以唯一标识记录的列(非空唯一键)作为主键;若不存在,则生成一个6字节的隐含字段rowid作为主键。

InnoDB为什么必须有主键:

  1. 存储结构为索引组织表
  2. 二级索引回表需要借助主键查询到记录

3.2 二级索引(辅助索引)

非主键索引也称为二级索引,其叶子节点保存的是二级索引及其对应的聚簇索引,即主键。InnoDB会把主键字段放到索引定义字段后面,当然同时也会去重,若主键的部分列(复合索引)已经包含二级索引中,则不会再重复记录该列。

利用二级索引进行查询时,相较于利用主键查询,需要一次额外的回表操作——得到主键之后,再到聚簇索引树中进行查找。

利用覆盖索引可以减少回表次数,同时InnoDB也引入了索引下推MRR来降低回表次数和开销,前者将部分查询条件(索引和主键包含的列)下推到引擎层,InnoDB在回表前会先进行过滤;后者在回表前先对主键进行排序,以降低随机IO(回表还是一行一行的进行主键查询)。

3.3 一颗B+树可以存储多少数据

InnoDB采用了B+树作为存储的数据结构,这是一种为外存随机IO优化的平衡多路搜索树,是一颗“N叉“树。N的大小取决于数据页和索引的大小。

在InnoDB中,数据页的地址指针为6个字节,在未开启压缩页的情况下,数据页的大小为16k。假设主键索引采用8个字节的bigint类型,那么每个索引大概占据14字节。在B+树的非叶子节点中,忽略数据页的固定开销,每个数据页大概可以存放16384➗14=1170个索引,估算为1024。

假设每条记录的大小为1k,那么在叶子结点的每个数据页可以存放16条记录。

一个高度为3的B+树可以存放:1024102416=1677216条记录,大小为1024102416KB=16GB。

所以在InnoDB中B+树高度一般为1-3层,它就能满足千万级的数据存储。在查找数据时一次⻚的查找代表一次IO,所以通过主键索引查询通常只需要1-3次IO操作即可查找到数据。考虑到树根的数据页一般位于内存,那么IO次数将更少。

4. 哈希索引

见 InnoDB整体架构与关键技术 中的自适应哈希索引AHI。

5. 全文检索

全文检索(Full-Text Search)利用倒排索引(Inverted Index)来实现。倒排索引可以根据单词快速获取包含这个单词的文档矩阵,其常见格式是:key = 单词, value = 文档ID + 单词位置。

InnoDB将倒排索引存储于辅助表中,且为了提高并行度,使用了多张辅助表。

MySQL使用MATCH (‘Col Name’) AGAINST (‘Search’)来进行全文检索,结果集依据相关性降序排列。相关度依据单词在文档中是否出现、出现的次数、多少其他文档包含该单词等因素计算。

猜你喜欢

转载自blog.csdn.net/cooper20/article/details/108636464