1) B+-tree的磁盘读写代价更低 

B+-tree的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。 

举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘快。而B+ 树内部结点只需要1个盘快。当需要把内部结点读入内存中的时候,B 树就比B+ 树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。

看节点定义可以看出来。

2) B+-tree的查询效率更加稳定

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

3)B+树还有一个最大的好处,方便扫库,B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持。这是数据库选用B+树的最主要原因。

“有很多基于频率的搜索是选用B树,越频繁query的结点越往根上走,前提是需要对query做统计,而且要对key做一些变化。”

B*-tree:

B*-tree是B+-tree的变体,在B+ 树非根和非叶子结点再增加指向兄弟的指针;B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)。

B-tree:

定义:m阶,子女节点个数从ceil(m/2)到m/2,关键字个数从ceil(m/2)-1到m-1,比子女节点个数刚好少1.

/**

 *  插入关键字伪代码:

1、找到应该插入位置的节点,一定是叶子节点,直接插入;

2、如果该叶子节点关键字个数大于m-1;分裂该叶子节点;

分裂节点伪代码:

1、分裂该节点,产生一个新节点;

2、将中间关键字插入父节点中;

3、如果父节点关键字个数大于m-1,递归分裂父节点,否则直接返回;

 *  如果name已经存在,返回false

 **/

/**

 *  删除关键字伪代码----最终是在叶子节点上删除数据;

    1、查找包含这个关键字key的节点node:

2、如果这个节点是内节点:

a 找到左子树中含有最大关键字的节点leafnode,及其最大的关键字keyx;

c 维护leafnode

b 维护这个节点

维护节点伪代码:

a 如果关键字满足要求,直接返回;

b 如果左右兄弟节点有足够多的关键字,向其借一个,返回;

c 如果左右兄弟节点都没有足够的关键字,合并一个兄弟节点,回溯维护父节点。

 *

 *  如果name已经存在,返回false

 **/

B+tree:

定义:内部节点全部是索引关键字,data都在叶子节点。

内部节点:子女节点个数为ceil[m/2]到m,关键字可以和子女节点对应也可以少1(不同的书有不同的说法).关键字i是子树i+1的最小关键字。

叶子节点:全部是key-value值,个数ceil(L/2)-->L( L << M in practice)。

根节点:单个或是2-->M个子节点。

实际中上:

每个节点通常占用一个I/O块;

1/2级节点常驻内存;

大多数情况很多内部节点的关键字个数少于(m-1),对内存的极大浪费。

各大IT公司面试常常问到的问题——海量数据问题。以前通常回答二级索引,即一级索引常驻内存,通过一级索引找到二级索引,读入内存,再通过二级索引找到最终要找的具体数据,而“索引”,一直设想的都是HASH,现在回头想来,HASH其实是不合适的。因为HASH只能提供映射,而不能提供范围信息。这个问题的正确答案应该是B树或者B+树。 

Insert()

/**

    1、找到叶子节点,直接插入;

    2、>L, 则SplitLeafNode 或者是 SplitRootNode()

    SplitLeafNode()

    1 Node ==> LeftNode( ceil(L/2) ) + Right( remaining );

    2 向父节点插入rightNode和right节点中最小的关键字 InsertInternalNode(key, rightNode)

    

    InsertInternalNode()

    1 直接插入关键字及其右子树;

    2 子女节点个数>M,则 SplitInternalNode()

    SplitInternalNode()

1 Node ==> Left( ceil(M/2)-1 ) + Key(中间) + Right( M/2 ) 

 

      2 InsertInternalNode( Key, Right ) 

 

   **/ 

 

  Delete() 

 

  /**

    感觉挺麻烦的,细节还是不太清楚

    1、找到叶子节点,删除,如果有内部节点包含这个关键字,则用叶子节点的最小值替换

    2、关键字个数小于<L:Rebalance();

    Rebalance()

    1、如果LeftSibling关键字个数>=ceil(L/2)+1,LendLeftSibling;

    2、LendRightSibling

    3、MergeLeafNode() or MergeInternalNode()

    

    MergeLeafNode: 注意parent的key只需要删除就可以了

    MergeInternalNode:注意parent的key要往下移动

 **/

参考:

http://en.wikipedia.org/wiki/B%2Btree