B+索引在数据库中有一个特点是高扇出性,因此在数据库中,B+树的高度一般都在2~4层,这也就是说查找某一键值的行记录时最多只需要2到4次IO。
插入
B+树的插入必须保证插人后叶子节点中的记录依然排序,同时需要考虑插人到B+树的三种情况,每种情况都可能会导致不同的插入算法。
| Leaf Page满 | Index Page满 | 操作 |
|---|---|---|
| No | No | 直接将记录插入到叶子节点 |
| Yes | No | 1. 拆分 Leaf Page 1. 将中间的节点放入到 Index Page中 1. 小于中间节点的记录放在左边 1. 大于或等于中间节点的记录放右边 |
| Yes | Yes | 1. 拆分 Leaf Page 1. 小于中间节点的记录放左边 1. 大于或等于中间节点的记录放右边 1. 拆分 Index Page 1. 小于中间节点的记录放左边 1. 大于中间节点的记录放右边 1. 中间节点放入上一层 Index Page |
基础B+数图
用户插入 28 这个键值,发现当前 Leaf Page 和 Index Page 都没有满,直接进行插入。
结果如图:
接着再插入70这个键值,这时原来的 Leaf Page 已经满了,但是 Index Page 还没有满,此时插入 Leaf Page后的情况为,50/55/60/65/70,并根据中间的值 60 来拆分叶子节点。
结果如图
最后插入键值95,这时Leaf Page 和 Index Page都满了,这时需要做两次拆分。
先根据75/80/85/90/95的中间值85进行拆分Leaf Page,75/80 作为一个Leaf Page,85/90/95作为一个Leaf Page;
再根据25/50/60/75/85的中间值60进行拆分Index Page,25/50作为一个中间的Index Page,60单独作为一个Index Page,75/85作为一个中间的Index Page。
结果如图
旋转插入
不管怎么变化,B+树总会保持平衡。但是为了保持平衡对于新插入的键值可能需要做大量的拆分页操作。因为B+树结构主要用于磁盘,页的拆分意味着磁盘的操作,所以应该在可能的情况下尽量减少页的拆分操作。因此,B+树同样提供了类似于平衡二叉树的旋转功能。
旋转发生在 Leaf Page 已经满,但是其他左右兄弟节点没有满的情况下。此时B+树不会急于去做拆分页的操作,而是将记录所在页的兄弟节点上。
例如,若插入键值70,其实B+树并不会急于拆分叶子节点,而是做旋转操作。
删除
B+树使用填充因子(fillfactor)来控制树的删除变化,50%是填充因子可设的最小值。B+树的删除操作同样必须保证删除后叶子节点中的记录依然排序,同插人一样,B+树的删除操作同样需要考虑三种情况,与插入不同的是,删除根据填充因子的变化来衡量。
B+树使用填充因子来控制页面的分裂和合并,设置数据占用页面空间的百分比,目的是为后面的数据预留一部分页空间,当有新数据时,可以放到预留的页空间中,避免分页的发生。
| 叶子节点小于填充因子 | 中间节点小于填充因子 | 操作 |
|---|---|---|
| No | No | 直接将记录从叶子节点上删除,如果该节点还是Index Page的节点,用该节点的有节点代替 |
| Yes | No | 1. 合并叶子节点和他的兄弟节点 1. 同时更新 Index Page |
| Yes | Yes | 1. 合并叶子节点和他的兄弟节点 1. 更新 Index Page 1. 合并 Index Page 和他的兄弟节点 |
删除键值70,60/65/70 这一叶子节点,删除后70后,填充因子为50%,中间节点75/85不受影响,填充因子保持50%没有变化,直接删除即可。
结果如图:
接着删除键值25,在Leaf Page上25/28/30,删除25后,填充因子为50%,同时25也是中间节点,所以需要将25删除后,再将右边的28代替Index Page原来25的位置。
结果如图:
最后删除键值60,删除键值为60的记录后,60/65的填充因子为25%,小于50%,此时需要合并其他兄弟节点,同样60还在Index Page 上,删除60后,60所在的Index Page 填充因子为0%,小于50%,所以也需要Index Page 做合并操作。
