【重难点】【MySQL 04】索引的基本原理、Hash索引和B+树索引、数据库为什么使用B+树而不是B树
文章目录
- 【重难点】【MySQL 04】索引的基本原理、Hash索引和B+树索引、数据库为什么使用B+树而不是B树
- 一、Hash索引和B+树索引
- 二、MySQL 索引为什么使用B+树
- 1.B树
- 2.B+ 树
一、Hash索引和B+树索引
数据结构
B+ 树是一个平衡的多叉树,从根结点到每个叶子结点的高度差值不超过 1,而且同层级的结点间有指针相互连接
在 B+ 树上的常规检索,从根结点到叶子结点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双指针快速左右移动,效率非常高。因此,B+ 树索引被广泛应用于数据库、文件系统等场景
Hash 索引很简单,就是采用一定的 Hash 算法,把键值换算成新的 Hash 值,检索时不需要类似 B+ 树那样从根结点到叶子结点逐级查找,只需一次 Hash 算法即可立刻定位到相应的位置,速度非常快。InnoDB 中采用拉链法解决哈希冲突
对比
- 如果是等值查询,那么 Hash 索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值。当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据
- 如果是范围查询检索,这时候 Hash 索引就毫无用武之地了,因为原先是有序的键值,经过 Hash 算法后,顺序都打乱了,就没办法再利用索引完成范围查询检索
- 同理,Hash 索引也无法利用索引完成排序,以及 like 这样的部分模糊查询,这种部分模糊查询其实本质上也是范围查询
- Hash 索引不支持复合索引,对于复合索引来说,Hash 索引再计算 Hash 值的时候是将索引键合并后再一起计算 Hash 值,不会针对每个索引单独计算 Hash 值。因此,如果用到复合索引的一个或者几个索引时,索引会失效
- B+ 树索引的关键字检索效率比较平均,不像 B 树那样波动幅度大,在有大量重复键值的情况下,Hash 索引的效率也是极低的,因为存在哈希冲突问题
应用场景
通常,B+ 树索引结构适用于绝大多数场景
如果数据离散型高、基数大,且为等值查询的时候,Hash 索引特别有优势
二、MySQL 索引为什么使用B+树
1.B树
思考为什么使用 B+ 树之前我们先思考为什么要使用树结构存储?我直接给出答案吧,因为树的查询效率高,而且可以保持有序
如果需要查询效率高,二叉查找树不是更好的选择,为什么要使用 B 树呢?这就需要好好分析了
二叉查找树查询的时间复杂度是 O(logN),性能已经足够高了,从算法逻辑上来讲,二叉查找树的查找速度和比较次数都是最小的,但是数据库需要考虑一个现实问题:磁盘 IO。数据库索引是存储在磁盘上的,当数据量比较大的时候,索引可能有几个 G 甚至更多。当我们利用索引查询的时候,显然不能把整个索引全部加载到内存,我们能做的只有逐一加载每一个磁盘页,这里的磁盘页对应着索引树的结点
如果我们利用二叉查找树作为索引结构,那么我们每判断一次就要进行一次磁盘 IO,最坏的情况是我们要查找的数据在叶子结点,也就是说磁盘 IO 的次数是由树的高度决定的。既然如此,我们就需要降低树结构的高度,从而减少磁盘 IO 次数,把原本 “瘦高” 的树结构变得 “矮胖”,这就是 B 树的特征之一
B 树介绍
B 树(Balance Tree)是一种多路平衡查找树,它的每一个结点最多包含 k 个孩子,k 被称为 B 树的阶,k 的大小取决于磁盘页的大小
一个 m 阶的 B 树具有如下几个特征:
- 根结点至少有两个子结点
- 每个中间结点都包含 k - 1 个元素和 k 个子结点,其中 m/2 <= k <= m
- 每一个叶子结点都包含 k - 1 个元素,其中 m/2 <= k <= m
- 所有的叶子结点都位于同一层
- 每个结点中的元素从小到大排列,结点当中 k - 1 个元素正好是 k 个子结点包含的元素的值域分划
B 树在查询中的比较次数其实不比二叉查找树少,尤其当单一结点中的元素数量很多时,可是相比磁盘 IO 的速度,内存中的比较耗时几乎可以忽略。所以只要树的高度足够低,IO 次数足够少,就可以提升查找性能。因此,结点内部元素多一些也没有关系,仅仅是多了几次内存交互,只要不超过磁盘页的大小即可,这就是 B 树的优势之一
B 树无论是插入还是删除,B 树始终能维持多路平衡,这时 B 树的一大优势
至此,我们了解了 B 树相较于其它树结构的优势。B 树主要应用于文件系统,其实部分数据库索引用的就是 B 树,而不是 B+ 树,比如著名的非关系型数据库 MongoDB
而大部分关系型数据库,比如 MySQL 则使用 B+ 树作为索引,这也就回到了我们的主题,为什么 MySQL 索引使用 B+ 树?
2.B+ 树
同样,我们还是先介绍一下 B+ 树的概念
B+ 树介绍
B+ 树是基于 B 树的一种变体,有着比 B 树更高的查询性能
一个 m 阶的 B+ 树具有如下几个特征:
- 有 k 个子树的中间结点包含有 k 个元素(B 树是 k - 1),每个元素不保存数据,只用来索引,所有数据都保存在叶子结点
- 所有的叶子节点中包含了全部元素的信息,以及指向含有这些元素记录的指针,且叶子结点本身依据关键字的大小从小到大地链接
- 所有的中间结点元素都同时存在于子结点,在子结点元素中是最大或最小元素
B+ 树还具有一个特点,这个特点是在索引之外,却是至关重要的特点,那就是卫星数据(Satellite Information)的位置。所谓卫星数据,指的是索引元素所指向的数据记录,比如数据库中的某一行。在 B 树中,无论中间结点还是叶子结点都带有卫星数据,而在 B+ 树中,只有叶子结点带有卫星数据,其余中间结点仅仅是索引没有任何数据关联,如下两张图可以清晰地看出两者的区别:
补充一点:在数据库的聚簇索引中,叶子结点直接包含卫星数据;在非聚簇索引中,叶子结点带有指向卫星数据的指针
B 树与 B+ 树的对比
在单行查询的时候,B+ 树会自顶向下逐层查找结点,最终找到匹配的叶子结点。这看起来和 B 树差不多,但其实有两点不同。首先,B+ 树的中间结点没有卫星数据,所以同样大小的磁盘页可以容纳更多的结点元素,这就意味着,数据量相同的情况下,B+ 树的结构比 B 树更加 “矮胖”,因此查询时 IO 次数也更少。其次,B+ 树的查询必须最终查找到叶子结点,而 B 树只要找到匹配元素即可,无论匹配元素处于中间结点还是叶子结点。因此,B 树的查找性能并不稳定(最好情况是只查根结点,最坏情况是查到叶子结点)。而 B+ 树的每一次查找都是稳定的
我们再来看看范围查询。B 树做范围查询只能依靠繁琐的中序遍历,而 B+ 树只需要在链表上做遍历即可:即先自顶向下找到范围的下限,再通过链表指针遍历到目标元素
除了查询,还有插入和删除操作,因为 B+ 树的叶子结点包含所有元素,并且以有序的链表结构存储,这样大大提高了增删结点的效率
综上,B+ 树相比 B 树的优势:
- 磁盘 IO 次数更少
- 查询性能稳定
- 范围查询简便
- 增删结点时,效率更高