raft&bitcask 键值存储数据库

关于用 go 实现一个由 raft 分布式一致性协议和 bitcask 存储模型搭建的分布式键值存储数据库。

raft

raft 的实现来自于 MIT 6.824 课程实践 lab 的调整。主要集中于

  1. 内部调用方式上用 grpc 协议取代了该 lab 原有的 channel 模拟。
  2. 对于命令实现的调整。

命令应被传入当前的 leader 处,在这里由一个 subscriber 的机制来完成。该 subscriber 与应用处于同一层级,会接收到 raft 集群内部的命令但不会进行响应,只负责来记录 leader 的变更备应用使用。

只要命令通过 RPC 调用恰当的传入当前 leader,该 leader 确认后就会返回命令接受,立即开始日志同步,而调用该命令者则认为该命令已经被执行。但实际上,命令的真正执行时间,是 leader 确认半数节点接受命令后 apply 该日志时,则该日志所代表的命令会返回给真正的执行方开始执行。

当然,这边的执行方可以是任何实现了定义的 Command 接口的结构。

bitcask

bitcask 是真正的执行方。

bitcask 模型是一个经典的键值对存储模型。其使用日志形式管理键值对存储。

其核心在于追加写模式和 merge hint 过程。

  1. 追加写,不修改之前的内容保证了键值对结构的紧密排列和高效率。
  2. merge hint 过程,则保证了查找时的效率。

merge 即从前往后遍历之前的文件,只保留不被删除和覆盖的键值对一步步生成新的文件。而 hint 即 merge 过程生成的索引。其值存储的是真正的值所在的文件和偏移量。hint 文件会在下一次重启时起到恢复内存索引的作用。

在内存中,我们还是使用索引机制,索引与 hint 文件类似,不同点在于我们可以使用不同的方式构建索引,以实现快速查找。比如红黑树,b树等。

内部实现上我们还是围绕接口进行,如文件读写形式(传统 IO,mmap,iouring 等)和内存索引(Btree,HashMap)等。保证了可拓展性和模块化。

server

接着,我们需要划分宏观上的实现形式:即一个机器(或者说一个 provider )拥有一个 raft client 和 bitcask。多个 provider 组成一个 cluster,内部使用 raft 完成通信。多个 cluster 组成一个 server ,对外由客户端使用一致性哈希在不同 cluster 间完成负载均衡。

源码如下:
https://github.com/iku50/raft-kv


raft&bitcask 键值存储数据库
https://iku50.github.io/2024/06/26/raft/
作者
iku50
发布于
2024年6月26日
许可协议