目录
简述
索引是一种数据结构,用于帮助我们在大量数据中快速定位到我们想要查找的数据。索引可以比喻成是图书的目录。为了了解索引为什么能提高效率,我们需要先了解索引的结构。在Mysql的InnoDB引擎中,索引使用的是B+树数据结构。而B+树是由二叉查找树、平衡二叉树和B树三种数据结构演化而来。
二叉查找树
二叉查找树有以下性质:
1、任意节点左子树不为空,则左子树的值均小于根节点的值;
2、任意节点右子树不为空,则右子树的值均大于于根节点的值;
3、任意节点的左右子树也分别是二叉查找树;
4、没有键值相等的节点;
上图中,我们为 user 表(用户信息表)建立了一个二叉查找树的索引。
图中的圆为二叉查找树的节点,节点中存储了键(key)和数据(data)。键对应 user 表中的 id,数据对应 user 表中的行数据。
二叉查找树的特点就是任何节点的左子节点的键值都小于当前节点的键值,右子节点的键值都大于当前节点的键值。顶端的节点我们称为根节点,没有子节点的节点我们称之为叶节点。
如果我们需要查找 id=12 的用户信息,利用我们创建的二叉查找树索引,查找流程如下:
将根节点作为当前节点,把 12 与当前节点的键值 10 比较,12 大于 10,接下来我们把当前节点>的右子节点作为当前节点。
继续把 12 和当前节点的键值 13 比较,发现 12 小于 13,把当前节点的左子节点作为当前节点。
把 12 和当前节点的键值 12 对比,12 等于 12,满足条件,我们从当前节点中取出 data,即 id=12,name=xm。
利用二叉查找树我们只需要 3 次即可找到匹配的数据。如果在表中一条条的查找的话,我们需要 6 次才能找到。
平衡二叉树(AVL树)
上面的二叉查找树,有一个隐患,由于二叉查找树是由n个节点随机构成,所以,对于某些情况,二叉查找树会退化成一个有n个节点的线性链:
这就造成二叉查找树的性能不稳定。为了解决这个问题,需要二叉查找树保持平衡,这就需要二叉平衡树。二叉平衡树在二叉查找树的基础上,要求每个节点的左右子树的高度差不能超过1,如果插入过程中,发现高度差超过1时,会通过旋转来实现平衡。
平衡二叉树的缺点在于维护这种平衡所需要的代价很高,所以实际应用中使用的不多,更多地方使用的是追求局部而不是非常严格整体平衡的红黑树。
B树
B树性质:
1、定义任意非叶子结点最多只有M个儿子,且M>2;
2、根结点的儿子数为[2, M];
3、除根结点以外的非叶子结点的儿子数为[M/2, M];
4、每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字)
5、非叶子结点的关键字个数=指向儿子的指针个数-1;
6、非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
7、非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树;
8、所有叶子结点位于同一层;
查找流程:
假如我们要查找 id=28 的用户信息,那么我们在上图 B 树中查找的流程如下:
-
先找到根节点也就是页 1,判断 28 在键值 17 和 35 之间,那么我们根据页 1 中的指针 p2 找到页 3。
-
将 28 和页 3 中的键值相比较,28 在 26 和 30 之间,我们根据页 3 中的指针 p2 找到页 8。
-
将 28 和页 8 中的键值相比较,发现有匹配的键值 28,键值 28 对应的用户信息为(28,bv)。
从上图可以看出,相对于平衡二叉树,B树每个节点存储更多的键值和数据,且每个节点拥有更多的子节点。一般情况下,表和索引的数据都保存在磁盘中,和内存相比,磁盘慢千万倍,所以为了提高效率,要尽可能减少从磁盘读取数据的次数。Mysql中,数据读取的基本单位是页。相比于平衡二叉树,B树高度更低,读取磁盘次数更少,性能更高。
B+树
B+树性质:
1、非叶子节点的子树指针与关键字个数相同;
2、非叶子节点的子树指针p[i],指向关键字值属于[k[i],k[i+1]]的子树.(B树是开区间,也就是说B树不允许关键字重复,B+树允许重复);
3、为所有叶子节点增加一个链指针;
4、所有关键字都在叶子节点出现(稠密索引). (且链表中的关键字恰好是有序的);
5、非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层;
B+树是B树的变种,与B树对比,B+树主要有以下不同:
1、B+ 树非叶子节点上是不存储数据的,仅存储键值,而 B 树节点中不仅存储键值,也会存储数据
2、 B+ 树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的
为什么B+树比B树的效率更高?
1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。
2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
参考资料: