心里没点B树?

B+树是一种基于B树的一种变体,有着比B树更高的查询性能。

一个m阶的B+树特征

1.有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。

2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

3.所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6k6yK4Ac-1594008866596)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200623154607930.png)]

首先,每一个父节点的元素都出现在子节点中,是子节点的最大(或最小)元素;

根节点最大元素是整个B+树的最大元素,无论增删多少元素,始终要保持最大元素在根节点当中。

叶子节点包含了全量元素信息,并且叶子节点都带有指向下一个节点的指针,形成了一个有序链表;中间节点仅仅是索引,没有任何数据关联;

需要补充的是,在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。

B+树的好处主要体现在查询性能上,在单元素查询的时候,B+树会自顶向下逐层查找节点,最终找到匹配的叶子节点。

比如说我们要查询的元素是3,需要经过三次磁盘IO。看起来和B树差不多,但是B+树没有卫星数据,所以同样大小的祠旁页可以容纳更多的节点元素。这就意味着,数据量相同的情况下,B+树的结构比B-树更加矮胖,因此查询时候IO次会更少;其次B+树的查询必须最终查询到叶子节点,而B树子要找到匹配元素即可,无论匹配元素处于中间节点还是叶子节点;因此B树的查找性能并不稳定,最好的情况是只查根节点,最坏的情况是查到叶子节点,而B+树的每一次查找都是稳定的;

在范围查找中,B树只能依靠繁琐的中序遍历,而B+树子需要在链表上做遍历即可;

B+树比B-数的优势有三个,IO次数更少,查询性能稳定,范围查询简单;

mysql底层存储是用B+树实现的,因为内存中B+树是没有优势的,但是一到磁盘,B+树的威力就出来了。

为什么Mysql用B+树,MongoDB用B树呢?

B树和B+树
开头,我们先回忆一下,B树和B+树的结构以及特点,如下所示:
B树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WwtHfpMM-1594008866609)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624103004736.png)]

注意一下B树的两个明显特点
树内的每个节点都存储数据

叶子节点之间无指针相邻

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gm5KCArd-1594008866610)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624103026970.png)]

注意一下B+树的两个明显特点

数据只出现在叶子节点

所有叶子节点增加了一个链指针

针对上面的B+树和B树的特点,我们做一个总结
(1)B树的树内存储数据,因此查询单条数据的时候,B树的查询效率不固定,最好的情况是O(1)。我们可以认为在做单一数据查询的时候,使用B树平均性能更好。但是,由于B树中各节点之间没有指针相邻,因此B树不适合做一些数据遍历操作。

(2)B+树的数据只出现在叶子节点上,因此在查询单条数据的时候,查询速度非常稳定。因此,在做单一数据的查询上,其平均性能并不如B树。但是,B+树的叶子节点上有指针进行相连,因此在做数据遍历的时候,只需要对叶子节点进行遍历即可,这个特性使得B+树非常适合做范围查询。

因此,我们可以做一个推论:没准是Mysql中数据遍历操作比较多,所以用B+树作为索引结构。而Mongodb是做单一查询比较多,数据遍历操作比较少,所以用B树作为索引结构。

那么为什么Mysql做数据遍历操作多?而Mongodb做数据遍历操作少呢?
因为Mysql是关系型数据库,而Mongodb是非关系型数据。

那为什么关系型数据库,做数据遍历操作多?

而非关系型数据库,做数据遍历操作少呢?
我们继续往下看

关系型VS非关系型
假设,我们此时有两个逻辑实体:学生(Student)和班级(Class),这两个逻辑实体之间是一对多的关系。毕竟一个班级有多个学生,一个学生只能属于一个班级。
关系型数据库
我们在关系型数据库中,考虑的是用几张表来表示这二者之间的实体关系。常见的无外乎是,一对一关系,用一张表就行。一对多关系,用两张表。多对多关系,用三张表。
那这里,我们需要用两张表表示二者之间逻辑关系,如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X1aKuNUY-1594008866617)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624103149936.png)]

那我们,此时要查cname为1班的班级,有多少学生怎么办?
假设cname这列,我们建了索引!
执行SQL,如下所示!
SELECT *
FROM t_student t1, (
SELECT cid
FROM t_class
WHERE cname = ‘1班’
) t2
WHERE t1.cid = t2.cid
而这,就涉及到了数据遍历操作!

因为但凡做这种关联查询,你躲不开join操作的!既然涉及到了join操作,无外乎从一个表中取一个数据,去另一个表中逐行匹配,如果索引结构是B+树,叶子节点上是有指针的,能够极大的提高这种一行一行的匹配速度!

有的人或许会抬杠说,如果我先执行

SELECT cid
FROM t_class
WHERE cname = ‘1班’
获得cid后,再去循环执行

SELECT *
FROM t_student
WHERE cid = …
就可以避开join操作呀?

对此,我想说。你确实避开了join操作,但是你数据遍历操作还是没避开。你还是需要在student的这张表的叶子节点上,一遍又一遍的遍历!

那在非关系型数据库中,我们如何查询cname为1班的班级,有多少学生?
非关系型数据库
有人说,你可以这么设计?也就是弄两个集合如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JbMcgt6v-1594008866621)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624103213015.png)]

然后,执行两次查询去获得结果!一次去class集合查,获得id后再去student集合查。

确实,这么设计是可以的,我没说不行。只是不符合非关系型数据库的设计初衷。在MongoDB中,根本不推荐这么设计。虽然,Mongodb中有一个 l o o k u p 操 作 , 可 以 做 j o i n 查 询 。 但 是 理 想 情 况 下 , 这 个 lookup操作,可以做join查询。但是理想情况下,这个 lookupjoinlookup操作应该不会经常使用,如果你需要经常使用它,那么你就使用了错误的数据存储了(数据库):如果你有相关联的数据,应该使用关系型数据库(SQL)。

因此,正规的设计应该如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31tN0PnF-1594008866623)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624103248475.png)]

假设name这列,我们建了索引!
我只需执行一次语句
db.class.find( { name: ‘1班’ } )
这样就能查询出自己想要的结果。

而这,就是一种单一数据查询!毕竟你不需要去逐行匹配,不涉及遍历操作,幸运的情况下,有可能一次IO就能够得到你想要的结果。

因此,由于关系型数据库和非关系型数据的设计方式上的不同。导致在关系型数据中,遍历操作比较常见,因此采用B+树作为索引,比较合适。而在非关系型数据库中,单一查询比较常见,因此采用B树作为索引,比较合适。

单一节点存储的元素更多、所以B+树更矮胖,平均下来B+树的IO更少,更稳定。 MongoDB比MySQL快,根本原因是因为MongoDB将大部分甚至全部数据直接放到内存中来使用,甚至将数据暂存到内存中,而MySQL主要使用磁盘,缓冲池的内部逻辑也比较复杂,自然MongoDB快,MySQL慢。如果内存不足MongnoDB会频繁的在内存和硬盘之间进行数据交换(类似于MySQL),此时磁盘IO要比MySQL的磁盘IO要高。 你可以理解为MySQL为纯磁盘类型数据库(不用担心数据丢失) MongoDB为半内存半磁盘类型数据库 Redis为纯内存式数据库 所以查询性能redis > MongoDB > MySQL

B-树和B+树最重要的一个区别就是B+树只有叶节点存放数据,其余节点用来索引,而B-树是每个索引节点都会有Data域。
这就决定了B+树更适合用来存储外部数据,也就是所谓的磁盘数据。
从Mysql(Inoodb)的角度来看,B+树是用来充当索引的,一般来说索引非常大,尤其是关系性数据库这种数据量大的索引能达到亿级别,所以为了减少内存的占用,索引也会被存储在磁盘上。
那么Mysql如何衡量查询效率呢?磁盘IO次数,B-树(B类树)的特定就是每层节点数目非常多,层数很少,目的就是为了就少磁盘IO次数,当查询数据的时候,最好的情况就是很快找到目标索引,然后读取数据,使用B+树就能很好的完成这个目的,但是B-树的每个节点都有data域(指针),这无疑增大了节点大小,说白了增加了磁盘IO次数(磁盘IO一次读出的数据量大小是固定的,单个数据变大,每次读出的就少,IO次数增多,一次IO多耗时啊!),而B+树除了叶子节点其它节点并不存储数据,节点小,磁盘IO次数就少。这是优点之一。
另一个优点是什么,B+树所有的Data域在叶子节点,一般来说都会进行一个优化,就是将所有的叶子节点用指针串起来。这样遍历叶子节点就能获得全部数据,这样就能进行区间访问啦。

至于MongoDB为什么使用B-树而不是B+树,可以从它的设计角度来考虑,它并不是传统的关系性数据库,而是以Json格式作为存储的nosql,目的就是高性能,高可用,易扩展。首先它摆脱了关系模型,上面所述的优点2需求就没那么强烈了,其次Mysql由于使用B+树,数据都在叶节点上,每次查询都需要访问到叶节点,而MongoDB使用B-树,所有节点都有Data域,只要找到指定索引就可以进行访问,无疑单次查询平均快于Mysql(但侧面来看Mysql至少平均查询耗时差不多)。

二叉树查找树:

基于二叉查找树的这种特点,我们在查找某个节点的时候,可以采取类似于二分查找的思想,快速找到某个节点。n 个节点的二叉查找树,正常的情况下,查找的时间复杂度为 O(logn)。

之所以说是正常情况下,是因为二叉查找树有可能出现一种极端的情况

这种情况也是满足二叉查找树的条件,然而,此时的二叉查找树已经近似退化为一条链表,这样的二叉查找树的查找时间复杂度顿时变成了 O(n),可想而知,我们必须不能让这种情况发生,为了解决这个问题,于是我们引申出了平衡二叉树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5a37HK3n-1594008866625)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624110429230.png)]

平衡二叉树就是为了解决二叉查找树退化成一颗链表而诞生了,平衡树具有如下特点

1、具有二叉查找树的全部特性。

2、每个节点的左子树和右子树的高度差至多等于1。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uE2IR63k-1594008866627)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624110510339.png)]

于是,通过平衡树,我们解决了二叉查找树的缺点。对于有 n 个节点的平衡树,最坏的查找时间复杂度也为 O(logn)。

3、为什么有了平衡树还需要红黑树?
虽然平衡树解决了二叉查找树退化为近似链表的缺点,能够把查找时间控制在 O(logn),不过却不是最佳的,因为平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。

显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣,为了解决这个问题,于是有了红黑树,红黑树具有如下特点:
1、红黑树的特性
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。[这里指到叶子节点的路径]
包含n个内部节点的红黑树的高度是 O(log(n)).

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vLJIGVWI-1594008866629)(C:\Users\Zion Admin\AppData\Roaming\Typora\typora-user-images\image-20200624110840208.png)]

2、红黑树的使用场景
java中使用到红黑树的有TreeSet和JDK1.8的HashMap。

但是问题来了,为什么要使用红黑树,红黑树的插入和删除都要满足以上5个特性,而作非常复杂的操作。
原因:
红黑树是一种平衡树,他复杂的定义和规则都是为了保证树的平衡性。如果树不保证他的平衡性就是下图:很显然这就变成一个链表了。

保证平衡性的最大的目的就是降低树的高度,因为树的查找性能取决于树的高度。所以树的高度越低搜索的效率越高!
这也是为什么存在二叉树、搜索二叉树等,各类树的目的。

红黑树的旋转次数少,所以对于搜索、插入、删除操作较多的情况下,我们就用红黑树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值