baidu / braft

An industrial-grade C++ implementation of RAFT consensus algorithm based on brpc, widely used inside Baidu to build highly-available distributed systems.
Apache License 2.0
3.99k stars 886 forks source link

如何基于braft实现超低延迟存储系统? #302

Open rpbear88 opened 3 years ago

rpbear88 commented 3 years ago

各位好,

我们正在使用braft构建我们自建的存储系统,我们做了一个原型系统来验证braft的性能,原型系统的一些要点如下:

因此,整个服务端处理写IO的流程是这样的:

  1. 服务端收到写请求后从RDMA,其polling线程原地将数据得到后创建一个background的bthread(我们称之为task bthread)来运行任务。(由于运行创建该bthread的worker线程用于RDMA的pooling,没有机会来处理本地的_rq任务,所以最终该任务会被其他worker偷过去运行?)
  2. task bthread进入到我们业务状态机后提交给braft
  3. braft执行完成后调用on_apply,此时会将raft同步完成的请求提交给存储引擎(通过SPDK的ring队列),此处会产生worker thread和引擎thread间的数据交换
  4. 存储引擎从任务队列中获取任务后进行SPDK的提交和polling
  5. SPDK提交完成后,在存储引擎所在的pthread直接原地(没有起新的background bthread)调用done->Run进行数据回复

目前在单队列的4K写场景下,braft从得到任务到调用on_apply耗时约90us(这个结果显然不能满足实现低于100us的IO延迟的存储系统的要求),我们还在调查这个延迟具体消耗在什么位置。但是考虑到raft本身的一些weakness,我们想从原理上看看是否有更好的方案,下面是我们提出的一个方案(感谢@PFZheng提供的思路)。

元数据和数据使用不同的复制方式

普通的主从同步方式,从并发的角度来说性能是优于raft的,因为它没有顺序提交的问题,但是主从方式做replication的缺点在于:

  1. 数据不是强一致,如果leader切换的话,可能会出现数据倒转等问题,这在某些应用场景是不可接受的
  2. 对于冲突数据的修改,并发的请求不能确定各节点执行的order

从数据同步的角度来说,raft和主从协议性能损耗可以认为是一样的,在raft中leader的日志只要确定了log id,是可以并行发给follower的。但是在日志持久化阶段,raft日志必须是串行提交进日志系统,这是其一个缺陷。在apply阶段,从实现层面来说,不冲突的请求可以并行apply,但是目前实现一般都是顺序apply,因为会依此更新apply idx,所以apply的顺序不能乱。所以这里的一个解决思路可能并不是去并行apply,这样在既有的raft框架内改动也较大,而是尝试让apply尽可能快,那么这个顺序apply的开销就会很小,比如像redis那样纯内存的操作,那么性能也是可以做到非常强悍的。

由于我们的存储引擎对数据的修改采用的是非原地写(out-of-place write),我们考虑整个流程处理成如下步骤:

我们的改造主要有两个方面,第3步的改造,相当于让原本顺序执行的提交日志项变成了并行提交,理论上对性能提升有帮助。而apply阶段则变成纯内存操作,不涉及IO。

从原理上来说,我觉得raft的方案是有一个悲观锁的解决方式,用串行化来解决可能存在的争端但是在冲突不大的情况下性能不行。而结合主从复制的模式,相当于是一个乐观锁的解决方案。

很想听听大家对这个问题的解决方案的看法。

rpbear88 commented 3 years ago

这个方案的前提依然是,我们认为braft的实现原理上没有特别多的导致性能差的点(虽然实验数据看起来并不是这样,我们还在进行调整),依然认为最大的性能问题来自于raft算法本身。

而对braft来说,leader上本地日志提交和两个peer的replicator对log entry队列的争抢虽然有一些锁的保护,但是锁竞争并不会很大(和peer数相等)。

PFZheng commented 3 years ago

Raft的串行化阶段对这种超低延时的存储系统不够友好。这个改造方案里最复杂的其实是配置变更

pidb commented 1 year ago

@rpbear88 你们遇到的问题和阿里 polardb 团队很类似, shard-stoage 的数据库复制的时候需要 raft 乱序提交 + 乱序执行来降低延迟, 他们提出了parallel raft 算法, 但是我看了论文对比了一下 multi-paxos, 这个对 raft 改造很大... 所以你们可以考虑不基于共识的方案, 简单来说就是放弃 raft...