在彻底搞懂系列B-树、B+树、B-树、B*树_chai471793的博客-优快云博客_b+树 和 B树?这篇文章彻底看懂了!【图文】_架构师追风_51CTO博客 上的基础上进行整理参考;
各种树的对比和应用
B 树(Balance Tree)即为平衡树的意思
名称 | 对比说明: |
平衡二叉树 | 平衡二叉树又称 AVL 树,在满足二叉查找树特性的基础上,要求每个节点的左右子树的高度差不能超过 1; |
自平衡二叉树(红黑树) | 当我们插入或删除数据导致原本平衡的变成不平衡了,平衡二叉树会进行调整树上的节点来保持平衡; TreeMap 和 TreeSet 的实现就是红黑树数据结构; |
B树(又叫B-树) | 【B树和平衡二叉树】不同:B树属于【多叉树】又名【平衡多路查找树】(查找路径不只两个); |
B+树 | B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。 数据库索引技术里大量使用 B+树 的数据结构 ( InnoDB 和 mysql ); MyISAM 中的 B+ 树索引实现与 InnoDB 中的略有不同。在 MyISAM 中,B+ 树索引的叶子节点并不存储数据,而是存储数据的文件地址; |
B* 树 | B*树是B+树的变种,在B+树的基础上因其初始化的容量变大,使得节点空间使用率更高,而又存有兄弟节点的指针,可以向兄弟节点转移关键字的特性使得B*树额分解次数变得更少; |
二叉查找树
目标:二叉树查找 id=12 的用户信息;
将根节点作为当前节点,把 12 与当前节点的键值 10 比较,12 大于 10,接下来我们把当前节点>的右子节点作为当前节点。
继续把 12 和当前节点的键值 13 比较,发现 12 小于 13,把当前节点的左子节点作为当前节点。
把 12 和当前节点的键值 12 对比,12 等于 12,满足条件,我们从当前节点中取出 data,即 id=12,name=xm。
结论:利用二叉查找树我们只需要 3 次即可找到匹配的数据。如果在表中一条条的查找的话,我们需要 6 次才能找到。
构建索引要考虑的问题
- 因为内存的易失性。一般情况下,我们都会选择将 user 表中的数据和索引存储在【磁盘】这种外围设备中。
- 但是和内存相比,从磁盘中读取数据的速度会慢上百倍千倍甚至万倍,所以,我们应当【尽量减少从磁盘中读取数据】的次数。
- 另外,从磁盘中读取数据时,都是按照【磁盘块】来读取的,并不是一条一条的读。
所以:若每个磁盘块仅仅存储一个键值和数据,存储海量的数据会导致二叉树的节点将会非常多,高度也会极其高,我们查找数据时也会进行很多次磁盘 IO,我们查找数据的效率将会极低!
由此,产生了B树;
注意:一般根节点都是常驻内存的;
B 树
图中的每个【节点】称为【页】,页就是我们上面说的【磁盘块】,在 MySQL 中数据读取的基本单位都是页,所以我们这里叫做页更符合 MySQL 中索引的底层数据结构。
B 树相对于平衡二叉树,每个节点存储了更多的键值(key)和数据(data),并且每个节点拥有更多的子节点,子节点的个数一般称为【阶】,上述图中的 B 树为 3 阶 B 树,高度也会很低;
假如我们要查找 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+ 树
左图 B树, 右图 B+ 树
区别和改进:
- B+ 树【非叶子节点】上是不存储数据的,仅存储键值,而 B 树节点中不仅存储键值,也会存储数据;
- B+ 树的阶数是等于键值的数量的;
- B+ 树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的;B+ 树中各个页之间是通过【双向链表】连接的,叶子节点中的数据是通过【单向链表】连接;
这样做的原因:在数据库中页的大小是固定的,InnoDB 中页的默认大小是 16KB;
如果不存储数据只存储键值,那么就会存储更多的键值,相应的树的阶数(节点的子节点树)就会更大,树就会更矮更胖,如此一来我们查找数据进行磁盘的 IO 次数又会再次减少,数据查询的效率也会更快
B* 树
B+树节点满时就会【分裂】,而B*树节点满时会检查【兄弟节点】是否满(因为每个节点都有指向兄弟的指针),如果兄弟节点未满则向兄弟节点转移关键字,如果兄弟节点已满,则从当前节点和兄弟节点各拿出1/3的数据创建一个新的节点出来;
B* 树在B+树的基础上因其初始化的容量变大,使得节点空间使用率更高,而又存有兄弟节点的指针,可以向兄弟节点转移关键字的特性使得B*树额分解次数变得更少;