SOFAStacknode
Scalable Open Financial Architecture Stackgit
是蚂蚁金服自主研发的金融级分布式架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。github
本文为《剖析 | SOFAJRaft 实现原理》第二篇,本篇做者米麒麟,来自陆金所。《剖析 | SOFAJRaft 实现原理》系列由 SOFA 团队和源码爱好者们出品,项目代号:<SOFA:JRaftLab/>,目前领取已经完成,感谢你们的参与。算法
SOFAJRaft 是一个基于 Raft 一致性算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。数据库
SOFAJRaft :github.com/sofastack/s…缓存
SOFAJRaft-RheaKV 是基于 SOFAJRaft 和 RocksDB 实现的嵌入式、分布式、高可用、强一致的 KV 存储类库,SOFAJRaft 是基于 Raft 一致性算法的生产级高性能 Java 实现,支持 Multi-Raft-Group。SOFAJRaft-RheaKV 集群主要包括三个核心组件:PD,Store 和 Region。本文将围绕 SOFAJRaft-RheaKV 架构设计,存储概览,核心模块,使用场景以及基于 Raft 实现等方面剖析 SOFAJRaft-RheaKV 基于 SOFAJRaft 实现原理,阐述如何使用 Raft 协议支持 KV 存储类库功能特性:安全
SOFAJRaft-RheaKV 是一个轻量级的分布式的嵌入式的 KV 存储 Library, RheaKV 包含在 SOFAJRaft 项目里,是 SOFAJRaft 的子模块。SOFAJRaft-RheaKV 定位是嵌入式 jar 包方式嵌入到应用中,涵盖如下功能特性:bash
SOFAJRaft-RheaKV 存储类库主要包括 PD,Store 和 Region 三个核心组件,支持轻量级的状态/元信息存储以及集群同步,分布式锁服务使用场景:数据结构
SOFAJRaft-RheaKV 存储层为可插拔设计,实现 RawKVStore 存储接口,目前 StoreEngine 存储引擎支持 MemoryDB 和 RocksDB 两种实现:架构
SOFAJRaft-RheaKV 存储引擎基于 MemoryDB 和 RocksDB 实现 KV 存储入口:
com.alipay.sofa.jraft.rhea.storage.RawKVStore
com.alipay.sofa.jraft.rhea.storage.MemoryRawKVStore
com.alipay.sofa.jraft.rhea.storage.RocksRawKVStore复制代码
SOFAJRaft-RheaKV 数据强一致性依靠 SOFAJRaft 同步数据到其余副本 Replication, 每一个数据变动都会落地为一条 Raft 日志, 经过 Raft 协议日志复制功能将数据安全可靠地同步到同 Raft Group 的所有节点里。
SOFAJRaft-RheaKV 核心模块包括 KV 模块[RheaKVStore 基于 RegionRouteTable 路由表使用 RaftRawKVStore 存储 KeyValue],PD 模块[PlacementDriverServer 基于 StoreHeartbeat/RegionHeartbeat 心跳平衡节点分区 Leader 以及分裂]。
PD 模块主要参考 TIKV 的设计理念,目前只实现自动平衡全部节点的分区 Leader 以及自动分裂。
RheaKV 是基于 SOFAJRaft 实现的嵌入式、分布式、高可用、强一致的 KV 存储类库,TiKV 是一个分布式的 KV 系统,采用 Raft 协议保证数据的强一致性,同时使用 MVCC + 2PC 方式实现分布式事务的支持,二者如何基于 Raft协议实现 KV 存储?
RaftRawKVStore 是 RheaKV 基于 Raft 复制状态机 KVStoreStateMachine 的 RawKVStore 接口 KV 存储实现,调用 applyOperation(kvOperation,kvStoreClosure) 方法根据读写请求申请指定 KVOperation 操做,申请键值操做处理逻辑:
RheaKV 基于状态机 KVStoreStateMachine 的 RaftRawKVStore 存储 Raft 实现入口:
com.alipay.sofa.jraft.rhea.storage.RaftRawKVStore复制代码
RheaKV 运行在每一个 Raft 节点上面的状态机 KVStoreStateMachine 实现入口:
com.alipay.sofa.jraft.rhea.storage.KVStoreStateMachine 复制代码
RheaKV 是一个要保证线性一致性的分布式 KV 存储引擎,所谓线性一致性,一个简单的例子是在 T1 的时间写入一个值,那么在 T1 以后读必定能读到这个值,不可能读到 T1 以前的值。由于 Raft 协议是为了实现分布式环境下面线性一致性的算法,因此经过 Raft 很是方便的实现线性 Read,即将任何的读请求走一次 Raft Log,等 Log 日志提交以后在 apply 的时候从状态机里面读取值,必定可以保证此读取到的值是知足线性要求的。由于每次 Read 都须要走 Raft 流程,因此性能是很是的低效的,SOFAJRaft 实现 Raft 论文提到 ReadIndex 和 Lease Read 优化,提供基于 Raft 协议的 ReadIndex 算法的更高效率的线性一致读实现,ReadIndex 省去磁盘的开销,结合 SOFAJRaft 的 Batch + Pipeline Ack + 全异步机制大幅度提高吞吐。RaftRawKVStore 接收 get/multiGet/scan/getSequence 读请求都使用 Node
#
readIndex
(
requestContext
,
readIndexClosure
)
发起一次线性一致读请求,当可以安全读取的时候传入的 ReadIndexClosure 将被调用,正常状况从状态机中读取数据返回给客户端,readIndex 读取失败尝试应用键值读操做申请任务于 Leader 节点的状态机 KVStoreStateMachine,SOFAJRaft 保证读取的线性一致性。线性一致读在任何集群内的节点发起,并不须要强制要求放到 Leader 节点上面,将请求散列到集群内的全部节点上,下降 Leader 节点的读取压力。RaftRawKVStore 的 get 读操做发起一次线性一致读请求的调用:
// KV 存储实现线性一致读
public void get(final byte[] key, final boolean readOnlySafe, final KVStoreClosure closure) {
if (!readOnlySafe) {
this.kvStore.get(key, false, closure);
return;
}
// 调用 readIndex 方法,等待回调执行
this.node.readIndex(BytesUtil.EMPTY_BYTES, new ReadIndexClosure() {
@Override
public void run(final Status status, final long index, final byte[] reqCtx) {
if (status.isOk()) {
// ReadIndexClosure 回调成功,从 RawKVStore 调用 get 方法读取最新数据返回
RaftRawKVStore.this.kvStore.get(key, true, closure);
return;
}
// 特殊状况譬如发生选举读请求失败,尝试申请 Leader 节点的状态机
RaftRawKVStore.this.readIndexExecutor.execute(() -> {
if (isLeader()) {
LOG.warn("Fail to [get] with 'ReadIndex': {}, try to applying to the state machine.", status);
// If 'read index' read fails, try to applying to the state machine at the leader node
applyOperation(KVOperation.createGet(key), closure);
} else {
LOG.warn("Fail to [get] with 'ReadIndex': {}.", status);
// Client will retry to leader node
new KVClosureAdapter(closure, null).run(status);
}
});
}
});
}复制代码
TiDB 是 PingCAP 公司设计的开源分布式 HTAP (Hybrid Transactional and Analytical Processing) 数据库,TiDB 集群主要包括三个核心组件:TiDB Server,PD Server 和 TiKV Server。TiKV Server 负责存储数据,从外部看 TiKV 是一个分布式的提供事务的 Key-Value 存储引擎。存储数据的基本单位是 Region,每一个 Region 负责存储一个 Key Range(从 StartKey 到 EndKey 的左闭右开区间)的数据,每一个 TiKV 节点负责多个 Region。TiKV 使用 Raft 协议作复制,保持数据的一致性和容灾。副本以 Region 为单位进行管理,不一样节点上的多个 Region 构成一个 Raft Group,互为副本。数据在多个 TiKV 之间的负载均衡由 PD 调度,这里也是以 Region 为单位进行调度。TiKV 利用 Raft 来作数据复制,每一个数据变动都会落地为一条 Raft 日志,经过 Raft 的日志复制功能,将数据安全可靠地同步到 Group 的多数节点。TiKV 总体架构包括 Placement Driver,Node,Store 以及 Region 组件:
TiKV 使用 Raft 一致性算法来保证数据的安全,默认提供的是三个副本支持,这三个副本造成了一个 Raft Group。当 Client 须要写入 TiKV 数据的时候,Client 将操做发送给 Raft Leader,在 TiKV 里面称作 Propose,Leader 将操做编码成一个 Entry,写入到本身的 Raft Log 里面,称作 Append。Leader 也会经过 Raft 算法将 Entry 复制到其余的 Follower 上面,叫作 Replicate。Follower 收到这个 Entry 以后也会一样进行 Append 操做,顺带告诉 Leader Append 成功。当 Leader 发现此 Entry 已经被大多数节点 Append,认为此 Entry 已是 Committed 的,而后将 Entry 里面的操做解码出来,执行而且应用到状态机里面,叫作 Apply。TiKV 提供 Lease Read,对于 Read 请求直接发给 Leader,若是 Leader 肯定自身的 Lease 没有过时,那么直接提供 Read 服务不用执行一次 Raft 流程。若是 Leader 发现 Lease 已通过期,就会强制执行一次 Raft 流程进行续租而后再提供 Read 服务。TiKV 是以 Region 为单位作数据的复制,也就是一个 Region 的数据保存多个副本,将每个副本叫作一个 Replica。Replica 之间是经过 Raft 来保持数据的一致,一个 Region 的多个 Replica 保存在不一样的节点上构成一个 Raft Group,其中一个 Replica 做为此 Group 的 Leader,其余的 Replica 做为 Follower。全部的读和写都是经过 Leader 进行,再由 Leader 复制给 Follower。
本文围绕 SOFAJRaft-RheaKV 架构存储,模块流程以及基于 Raft 实现细节方面阐述 SOFAJRaft-RheaKV 基本原理,剖析 SOFAJRaft-RheaKV 如何使用 JRaft 一致性协议日志复制功能保证数据的安全和容灾,参考 TiKV 基于 Raft 算法实现了分布式环境数据的强一致性。
SOFA Meetup #2 上海站《使用 SOFAStack 快速构建微服务》期待你的参与❤~
5 月 26 日,本周日,SOFAStack 开源核心成员集体出动。本期咱们将侧重于各个落地的实际场景进行架构解析。
分布式事务 Seata 详解、与 Spring Cloud 生态的融合案例、使用 SOFAStack 快速构建微服务 Demo 实操、更有最新开源的《让 AI 像 SQL 同样简单 — SQLFlow Demo 》首秀,周日不见不散~
戳连接便可报名:tech.antfin.com/community/a…
公众号:金融级分布式架构(Antfin_SOFA)