2.2 Multi-Raft

关于 Multi-Raft

考虑到单机的容量有限,一些追求扩展性的系统,往往会将数据进行分片(Sharding),并将分片放置在不同的 Raft Group(复制组) 中,以达到每个分片都高可用的目的。Sharding + Multi-Raft 的架构比 Single-Raft 的架构在以下几个方面更具优势:

  • 扩展性:系统可以在需要的时候新增 Raft Group 用来存放分片,并将其运行在不同的磁盘或机器上,这样就具有很好的扩展性,理论上没有容量上限。

  • 性能:由于日志在 Leader 上都是被串行 apply,而 Multi-Raft 提供多个 Leader,可以提升整体的并发;此外,系统可以将 Leader 打散到各个节点,充分利用各机器的资源,提升整体吞吐。

各架构相关系统

single-raft: etcd, consul

multi-raft: CockroachDB, TiKV, Curve

图 2.2 single-raft 与 multi-raft

braft 中的 Multi-Raft

braft 允许一个进程管理多个 Raft Group,每个 Group 在逻辑上和物理上都是完全独立的,其实现如下:

  • 用户创建 braft::Node 时需要指定 Node 的 GroupIdPeerId

  • 在调用 Node::init 进行初始化时会将该 Node 加到全局的 NodeManager

  • 所有的 RPC 请求中都会携带目标 Node 的 GroupIdPeerId

  • NodeManager 根据请求中的 GroupIdPeerId 找到对应的 Node,然后再调用 Node 的相关方法处理请求。

图 2.3 braft Multi-Raft 实现

心跳

由于每个 Group 的 Leader 都需要给其 Follower 发送心跳,而心跳间隔一般都比较小(默认为 100 毫秒),所以如果单台机器上运行大量的 Group,会产生大量的心跳请求。

我们计算 3 副本构成的个 Group 在 1 秒内产生的心跳数:

随着 Group 和副本数的增加,心跳数会呈指倍数增长,比如运行 1 万个 3 副本的 Group,1 秒内将会产生 20 万个心跳。为此,像 CockroachDB 中 MultiRaft 实现会将每个节点之间的心跳进行合并,详见 Scaling Raft

需要注意的是,braft 开源版本还未实现心跳合并以及文档中提到的静默模式

图 2.4 CockroachDB 的 Multi-Raft 实现

随机写

虽然每个 Node 的日志都是顺序追加写,但是由于其都是独立的存储目录,所以当多个 Node 配置的存储目录位于同一块盘时,其对于该盘来说就相当于随机写。当然,braft 允许用户接管日志存储,用户可以自己实现顺序写逻辑。

图 2.5 Multi-Raft 的随机写

具体实现

用户指定 Node 信息

  • GroupId: 一个字符串, 表示这个复制组的 ID

  • PeerId:结构是一个 EndPoint,表示对外服务的端口,外加一个 Index (默认为 0)用于区分同一进程内的不同副本

添加至 NodeManager

用户在调用 Node::init 初始化节点时,会将该节点加入全局的 NodeManager 中:

RPC 指定 Node 信息

braft 中的 RPC 请求中都会携带目标 Node 的 GroupIdPeerId

选择对应 Node

RaftService 在收到 RPC 请求后,会让 NodeManager 根据请求中的 GroupIdPeerId 找到对应的 Node,然后再调用 Node 的相关方法处理请求:

参考

Last updated