sofastack / sofa-jraft

A production-grade java implementation of RAFT consensus algorithm.
https://www.sofastack.tech/projects/sofa-jraft/
Apache License 2.0
3.52k stars 1.12k forks source link

A high-throughput parallelized state machine #614

Open hzh0425 opened 3 years ago

hzh0425 commented 3 years ago

前言

我已和@fengjiachun 老师讨论过该优化方案,并会在暑期开始研发 若方案有任何问题,请帮忙指正,谢谢!

一 背景

状态机复制(State Machine Replication)是一种众所周知的实现容错服务的方法,提供高可用性和强一致性。

然而,SMR的缺陷也很明显:其无法提供高吞吐量。这种限制源于通用状态机复制技术的基本假设,即所有副本按照相同的总顺序依次执行请求,以确保副本之间的一致性。因此,我们有必要去探究如何并行化SMR,以从根本上解决问题。同时,该并行化方法不能对原有的共识算法有太多的变更。

解决的方案主要有两个方面:

下文将按照循序渐进的方式探讨Parallel SMR:

文章的结尾给出了参考论文的链接

实际上,该改进不仅仅运用于raft中,只要是基于一致性状态机的共识算法,都可以运用下文提到的改进方案,以从根本上提高系统的吞吐量

二 并行SMR设计架构

1.传统SMR架构

image-20210617111003207

如图为传统SMR的处理方式,总共分为三层:

性能分析:

通过上述的状态机架构分析,我们可以发现,阻碍状态机高吞吐量的根本原因在于状态机是依次执行提交的请求的,这显然违背了多核CPU时代的初衷,无法更好的利用服务器的CPU资源

2.并行化SMR架构

2.1架构简述

image-20210617112254964

如图所示,Parallel SMR,就是在共识层和状态机之间加入一个Parallelizer并行调度器,并且状态机不再是单线程执行,而是用一个线程池执行任务

Parallelizer的作用: 检测任务之间的依赖性,构建任务依赖图

为了更好的描述,上述的依赖关系和执行顺序,可以利用拓扑结构来表述,如图:

image-20210617113052612

那么,如何确定任务是否有依赖呢?论文[1]中提到了一种简单的检测方式:

例如一个kv存储系统,对于两次请求,如果这两次请求没有涉及相同的key,那就是无依赖的,反之就是有依赖(后文有更好的检测方式)

2.2接口定义

以下为Parallel SMR所定义的接口:

2.3运作流程

ok,现在我们来简单描述一下Parallel SMR的整体运作方式:

2.4一致性验证

显然,对于并行化状态机,我们最关心的问题是,这能否保证系统的线性一致性,以下为论文[1]中提到的验证逻辑的总结

3.高性能并行化SMR架构

3.1Parallel SMR的弊端

​ 为了并行化独立命令的执行,Parallel SMR向每个副本添加了一个Parallelizer调度器。每个副本上的并行器按总顺序发送命令,检查命令依赖关系,并将它们分发到工作线程池中以供执行。直观地看,如果拓扑图中的命令存在较少的依赖关系,将十分有利于并发执行。然而,在依赖关系图中添加新命令的成本与图中独立于新命令的命令数量成正比。如图,一个新命令g将首先与命令d和f进行比较;如果g与d无关,它将与c和b比较,以此类推。如果g独立于图中的每个命令,则它将与所有顶点进行比较。在高吞吐量状态机复制的上下文中,根据依赖关系图检测每个新命令之间的冲突的开销会严重阻碍性能。因此,接下去,我将描述处理命令依赖关系的更有效的方法。

image-20210617113052612

3.2优化思路

3.3性能取舍

​ 简化的依赖关系图的建立是一个性能的权衡。一方面,批处理减少了处理命令所需的开销(例如,更少的系统调用来交付命令,更少的边来存储图,更少的比较来确定依赖),这提高了性能。另一方面,简化的依赖关系图减少了并发性。在图2(b)中,独立的命令a和c不能并发的执行,因为批B1中的b依赖于批B2中的d,所以a,b必须在c和d之前执行,这就减少了并发性.

image-20210617144329221

3.4详细算法描述

image-20210617102323297

4.需要做的工作

三 参考资料

killme2008 commented 3 years ago

这个似乎就是 polardb parallel raft 的思路,本质上都是在 apply 阶段将依赖无关的 log 做并行 apply ?

hzh0425 commented 3 years ago

这个似乎就是 polardb parallel raft 的思路,本质上都是在 apply 阶段将依赖无关的 log 做并行 apply ?

我看了一下parallel raft,本质上确实是一样的,将无依赖的log 并行执行 image 但是parallel raft似乎是单个判断依赖的? 我觉得论文[2]里的观点会更好,利用batch+位图进行优化

killme2008 commented 3 years ago

@hzh0425 嗯,那是优化细节上的差异,思路上是一样的。 期待你的贡献