深入浅出存储引擎
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3.1 读多写少:基于B+树的存储引擎

顾名思义,B+树存储引擎内部采用B+树这种数据结构来存储数据。B+树的特点主要有三个:一是它属于多叉树,一个节点内部可以存储多个孩子节点;二是内部存储的数据是有序的,支持顺序遍历维护的数据;三是根据不同类型的节点,内部保存的数据有所不同,根节点、分支节点保存的是数据的索引信息,而叶子节点则保存的是原始数据。第三个特性是其他一般的数据结构所不具备的。

这里只需要记住它的特性即可,这样设计的目的主要是数据读取快,原因会在第4章详细分析。

B+树存储引擎目前的实现主要分为以下两种。

❑ 类InnoDB:泛指关系数据库中的各种存储引擎实现。

❑ 类BoltDB:泛指嵌入式场景中的KV类存储引擎实现。

这两类实现的区别主要在于采用B+树维护的数据是否实时/同步刷盘。在类InnoDB存储引擎内部,B+树维护的数据不是实时刷盘的。换言之,其内部B+树中的数据是异步刷盘的。这么做是为了保证读/写效率,因为实时落盘的开销是很大的。那它是如何保证数据持久化的呢?答案是在这类异步刷盘的实现中,都是采用预写WAL日志来保证的。

异步刷盘这类组件处理写请求的具体逻辑为:在一次写操作进来后,首先调用WAL模块将数据写入日志文件中存储起来,以保证数据的持久性,当机器宕机或者重启后可以用该日志来恢复数据。当WAL日志写成功后,会更新内存B+树中的数据。

上述操作完成后,就表示一次写入请求完成了,然后就可以响应客户端结果了(此处暂时不考虑主从数据之间的同步过程)。而后台会有单独的线程负责异步刷盘逻辑,它会按照一定的策略将内存中暂存的、已经修改完的脏页数据异步地写入磁盘中。当脏页数据写入磁盘后,对应的WAL日志通常也就没有用处了,此时就可以清理掉了。

通过上述过程可以看到,异步刷盘这类B+树存储引擎的实现过程非常复杂,需要考虑的边界条件非常多,稍有不慎就会导致新的问题。但其带来的好处是系统的写性能会有所提高。这在早期使用机械磁盘存储数据的时代是一种非常主流的设计思路,各大系统也验证了这种做法是可行的。

另外,同步刷盘虽然听上去比较“粗糙”,但是在一些嵌入式的基于B+树的存储引擎上有所应用。BoltDB项目就是这么做的,优点是实现简单、易维护。它在一些请求量不是很大的场景下非常实用,比如用作分布式系统中的WAL日志模块的底层实现或者一些一致性系统的磁盘状态机实现等。它处理写请求的逻辑如下:一个写请求进来后,从磁盘上加载对应节点的数据到内存中,然后开始修改该节点对应的数据。修改完成后就开始将内存中的脏页数据写入磁盘,最后响应上层用户。通常,这种方式会结合Mmap对磁盘文件进行内存映射,以加速访问。注意:这种组件往往配置批量接口进行写操作,这样性能会更佳。

对于B+树存储引擎,会在后面详细解读其原理。