B树、B+树和R树

来源:互联网 发布:南方电网邮箱域名 编辑:程序博客网 时间:2024/05/16 08:31

前言

动态查找树主要有:二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree),红黑树(Red-Black Tree ),B-tree/B+-tree/ B*-tree (B~Tree)。前三者是典型的二叉查找树结构,其查找的时间复杂度O(log2N)与树的深度相关,那么降低树的深度自然会提高查找效率。
但是咱们有面对这样一个实际问题:就是大规模数据存储中,实现索引查询这样一个实际背景下,树节点存储的元素数量是有限的(如果元素数量非常多的话,查找就退化成节点内部的线性查找了),这样导致二叉查找树结构由于树的深度过大而造成磁盘I/O读写过于频繁,进而导致查询效率低下,那么如何减少树的深度,一个基本的想法就是:采用多叉树结构
这样我们就提出了一个新的查找树结构——多路查找树。根据平衡二叉树的启发,自然就想到平衡多路查找树结构,也就是这篇文章所要阐述的第一个主题B~tree,即B树结构(后面,我们将看到,B树的各种操作能使B树保持较低的高度,从而达到有效避免磁盘过于频繁的查找存取操作,从而有效提高查找效率)。

B树索引

索引是数据库为了提高查询效率提供的一种冗余结构,保守计算数据库50%以上的调优可以通过调整索引来进行优化。B+树索引是我们日常工作最常用的索引,大家平时在工作中说的”索引”默认都是B+树索引。

B树和B+树

B树:即(所谓的B-树)搜索有可能在非叶子结点结束。
B+树:所有关键字都在叶子结点出现。

B+树索引的结构

B+树索引是基于二叉树结构的。B+树索引结构有3个基本组成部分:根节点、分支节点和叶子节点。其中根节点位于索引结构的最顶端,而叶子节点位于索引结构的最底端,中间为分子节点。

  • 叶子节点(Leaf node):包含条目直接指向表里的数据行。
  • 分支节点(Branch node):包含的条目指向索引里其他的分支节点或者是叶子节点。
  • 根节点(Branch node):一个B树索引只有一个根节点,它实际就是位于树的最顶端的分支节点。

可以用下图来描述B树索引的结构。其中,B表示分支节点,而L表示叶子节点。
这里写图片描述

分支节点块(包括根节点块)

1、 其所包含的索引条目都是按照顺序排列的(缺省是升序排列,也可以在创建索引时指定为降序排列)。

2、 每个索引条目(也可以叫做每条记录)都具有两个字段。第一个字段表示当前该分支节点块下面所链接的索引块中所包含的最小键值;第二个字段为四个字节,表示所链接的索引块的地址,该地址指向下面一个索引块。

3、 在一个分支节点块中所能容纳的记录行数由数据块大小以及索引键值的长度决定。比如从上图可以看到,对于根节点块来说,包含三条记录,分别为(0 B1)、(500 B2)、(1005 B3),它们指向三个分支节点块。其中的0、500和1005分别表示这三个分支节点块所链接的键值的最小值。而B1、B2和B3则表示所指向的三个分支节点块的地址。

叶子节点

1、 B+树索引的所有叶子块一定位于同一层上,这是由B树的数据结构定义的。Oracle 设计的B+树索引结构保证了B+树索引从根到叶子都有相等的分支节点,保证了B+树索引的平衡,这样就不会因为基表的数据插入和删除操作造成B树索引变得不平衡,从而影响索引的性能。
因此,从根块到达任何一个叶子块的遍历代价都是相同的; 索引高度是指从根块到达叶子块时所遍历的数据块的个数,通常,大多数的B+树索引的高度都是2到3,也就意味着,即使表中有上百万条记录,从索引中定位一个键字只需要2或3次I/O,索引越高,性能越差;

2、 叶子节点所包含的索引条目与分支节点一样,都是按照顺序排列的(缺省是升序排列,也可以在创建索引时指定为降序排列)

3、 每个索引条目(也可以叫做每条记录)也具有两个字段。第一个字段表示索引的键值,对于单列索引来说是一个值;而对于多列索引来说则是多个值组合在一起的。第二个字段表示键值所对应的记录行的ROWID,该ROWID是记录行在表里的物理地址。ROWID 是唯一的Oracle 指针,指向该行的物理位置,使用ROWID 是Oracle 数据库中访问行最快的方法。

4、 叶子节点其实是一个双向链表,每个叶子节点包含一个指向下一个和上一个叶子点的指针,这样可以方便地在一定范围内遍历索引。
这里写图片描述

B+树索引的访问

B树索引虽然是一个树状的立体结构,但其对应到数据文件里的排列当然还是一个平面的形式,也就是像下面这样。

/根/分支/分支/叶子/…/叶子/分支/叶子/叶子/…/叶子/分支/叶子/叶子/…/叶子/分支/…..

因此,当oracle需要访问某个索引块的时候,势必会在这个结构上跳跃移动。
当oracle需要获得一个索引块时,首先从根节点开始,根据所要查找的键值,从而知道其所在的下一层的分支节点,然后访问下一层的分支节点,再次同样根据键值访问再下一层的分支节点,如此这般,最终访问到最底层的叶子节点。
可以看出,其获得物理I/O块时,是一个接着一个,按照顺序串行进行的。在获得最终物理块的过程中,我们不能同时读取多个块,因为我们在没有获得当前块的时候是不知道接下来应该访问哪个块的。因此,在索引上访问数据块时,我们是按照顺序从一个索引块跳到另一个索引块,从而找到最终的索引块的。
那么对于全表扫描来说,则不存在访问下一个块之前需要先访问上一个块的情况。全表扫描时,oracle知道要访问所有的数据块,因此唯一的问题就是尽可能高效地访问这些数据块。因此,这时oracle可以采用同步的方式,分几批,同时获取多个数据块。这几批的数据块在物理上可能是分散在表里的。

B+树的插入操作

这里写图片描述

B+树相对于B树的优点

B树的非叶子节点也存储指向最终元素的指针;而B+树只有在叶子节点才能找到指向最终元素的指针。
为什么说B+-tree比B 树更适合实际应用中操作系统的文件索引和数据库索引?

  1. B+-tree的磁盘读写代价更低
    B+-tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
  2. B+-tree的查询效率更加稳定
    由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
  3. B+树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。

R树

R树在数据库等领域做出的功绩是非常显著的。它很好的解决了在高维空间搜索等问题。举个R树在现实领域中能够解决的例子吧:查找20英里以内所有的餐厅。如果没有R树你会怎么解决?一般情况下我们会把餐厅的坐标(x,y)分为两个字段存放在数据库中,一个字段记录经度,另一个字段记录纬度。这样的话我们就需要遍历所有的餐厅获取其位置信息,然后计算是否满足要求。如果一个地区有100家餐厅的话,我们就要进行100次位置计算操作了,如果应用到谷歌地图这种超大数据库中,我想这种方法肯定不可行吧。
R树就很好的解决了这种高维空间搜索问题。它把B树的思想很好的扩展到了多维空间,采用了B树分割空间的思想,并在添加、删除操作时采用合并、分解结点的方法,保证树的平衡性。因此,R树就是一棵用来存储高维数据的平衡树。
可以把R树看作是B+树在高维空间上的拓展。
具体可见R树