转自:
土法炼钢:怎么实现一个简单的B+Tree In-Diskwww.cnblogs.com![78d692c044a6f9789dec4c85b98d0614.png](https://i-blog.csdnimg.cn/blog_migrate/bedef4646716634b9951edc193cca146.png)
1. 写在前面
说起B+树,大家应该都很熟悉。B+树是一种平衡的多路搜索树,广泛在操作系统和数据库系统用作索引。相比于内存的存取速度,磁盘I/O存取的开销要高上几个数量级。而将B+树用作索引时,它可以在查找过程有效地减少磁盘I/O操作次数。
一般涉及B+Tree的书籍和文章都会提到它广泛用作外存的索引中,但是并没有详细讲解怎么实现。本文打算独辟蹊径,基于以前实现过的一个程序,介绍怎么实现一个简单可用的磁盘B+树。
本文对B+树的基础知识就不再赘言。磁盘中的B+树以文件的形式将整体都存放磁盘当中,使用时只在内存中缓存部份结构。在本文中,数据块和结点这两个词语都代表了B+树的一个结点。
当然本文作者水平有限,如有错误,还请大家给予指出。
2. 简单实现
将B+树存放于磁盘之中,第一步先定自义文件的格式,以便于读回的时候可以正确解析文件的数据。
2.1 B+树文件的格式
B+树的结点在内存中使用指针进行结点之间的串联,指针值是结点的第一个字节的虚拟内存地址。而对应的在磁盘中可以用所在的数据块的第一个字节相对文件头部的偏移量来标识。
索引文件主要分两个部份组成:
- 文件头:描述本文件的信息。
- 数据块:对应于B+树各个结点的数据。
2.1.1 文件头的格式:
typedef
struct tag_BTREE_HEADER
{
// base
int _magicNum;
size_t _orderNum;
size_t _nodeNum;
size_t _height;
// read pos
off_t _rootPos;
off_t _startLeafPos;
// freespace admin
size_t _freeBlockNum;
off_t _firstFreeBlockPos;
}BTREE_HEADER;
第一部份标识最基本的参数。其中:
- MagicNum: 定义一个魔数,使得程序在读取该文件时可以判断是不是自己可以处理的索引文件。
- OrderNum: B+树的阶数。
- NodeNum: B+树的总结点数量。
- Height: B+树的高度。
第二部份标识B+树的位置。其中:
- RootPos: 根结点所在的数据块的位置,它的值是相对于文件头部的偏移值。
- StartLeafPos: 最左边叶子的数据块的位置,它的值也是相对于文件头部的偏移值。在B+树中支持在叶子之间进行区间遍历,在这里标识第一个叶子结点的位置。
第三部份用来管理文件中的空闲块。保持一个空闲块的链表,可以使得分裂时需要新的块时可以优先从链表中摘得,如果链表中没有空闲块时再把块添加在文件的尾部。如果有一个数据块因为结点合并被废弃了,它可以简单地通过“头插法”被加入链表中。其中:
- FreeBlockNum: 用来标识目前文件中有多少数据块是未被使用的。可以设定F