浅谈分布式一致性协议 | 青训营笔记
这是我参与「第四届青训营」笔记创作活动的的第12天。
一、课程概述
- 一致性协议的定义
- Raft举例一致性协议如何工作
- 如何使用一致性协议构建KV系统
- 一致性协议发展方向
二、详细内容
1. 分布式系统的挑战
1.1 为何需要分布式系统
- 数据规模越来越大
- 服务的可用性要求越来越高:服务器宕机分布式系统能正常运行
- 快速迭代的业务要求系统足够易用:运用人员不需要感知下层细节;希望系统自动化完成修复扩容
1.2 分布式系统要求
- 高性能:可拓展、低时延、高吞吐
- 正确:一致性、易用、易于理解和修复系统
- 可靠:容错高、高可用
1.3 HDFS分布式系统
- DataNode:无状态,负责HDFS replication,实现高性能
- NameNode:存储元信息 重要的部分
1.4 KV案例
- 最简单机KV
- 接口:Get(key)->value; BatchPut([k1,k2,...], [v1,v2,...])
- client向服务器发送请求 服务器存储然后返回
- 可靠性:低容错低可用
- 正确性高:单进程,操作顺序执行
2. 一致性与共识算法
- 从复制开始
- Client RPC -> DBEngine RPC <-> DBEngine RPC
- 两台DBEngine完全状态一致
- 如果两个副本都能接受请求:可能发生交叉复制,顺序混乱
- 主副本定期拷贝全量数据到从副本/主副本拷贝操作到从副本
- state transfer
- transformation state machine
- 写操作
- 主副本把所有的操作打包成log,log写入持久化保存在磁盘
- 应用包装成状态机,只接受log为input
- 主副本确认log写入到副本机,状态机apply后返回客户端
- 读操作
- 直接读状态机要求写操作进入状态机后返回client
- 写操作复制完后直接返回,读操作block等待所有pending log进入状态机
- 若不遵循方案可能存在写入值读取不到
2.1 一致性
- KV像操作一台机器一样读到最近写入的值
- 一种模型/语义
- 约定一个分布式系统如何向外界应用提供服务
- 常见一致性模型
- 最终一致性:读取暂时读不到但最终会读到
- 线性一致性:最严格,一直一致,线性执行
- 分类:经常与应用本身有关,linarizability是最理想的
- 复制协议-当失效发生时
- 主副本失效
- 手动切换
- 容错低:服务停止
- 高可用:取决于发现切换人工速度
- 正确:操作从一台机器发起,返回前复制到另一个机器
- 当主副本失效时为了算法简单人工可以切换,保证较高可用性
- 如何保证主副本失效?若切换过程中主副本又接收client端请求,两个主副本不正确(复写log)
- 多个节点下少数节点宕机后仍然可以工作?
- fault-tolerance
- 手动切换
- 主副本失效
2.2 共识算法
- 当一个值一旦确定后,所有人都认同
- 错误总是发生:non-Byzantine fault
- 错误类型多:网络断开、分区、缓慢、重传、乱序;CPU、IO停止
- 容错 fault-tolerance
- 共识协议不等于一致性
- 应用层面不同一致性可以使用共识协议实现(故意返回旧值)
- 简单的复制协议也可以提供线性一致性
- 讨论时提到一致性至线性一致性
- 弱一致性可以使用简单复制算法实现
3. Raft
3.1 Paxos与Raft
- Paxos
- 一致性协议、正确;非liveness
- 没有完全解决问题,有可能不能达成一致(not terminate)
3.2 Raft原理
- 易于理解
- 使用RSM、Log、RPc概念
- 直接使用RPC对算法进行描述
- Strong leader-based:所有操作由leader发起
- 使用随机方法减少约束
- 正确性
- 形式化验证
- 拥有大量成熟系统
3.3 复制状态机RSM
- replicated state machine
- raft中所有consensus直接使用log作为载体
- commit index
- 一旦raft更新committed index,意味着index前所有log可以提交给状态机
- committed index不持久化,状态机volatile,重启后从第一条log开始
3.4 Raft角色
- leader:发现server有更高term
- follower发现leader有更高term
- candidate投票,leader获得更高的
3.5 Raft 复制
- 主节点向从节点复制,半数以上达成一致commit
3.6 从节点失效
- 主节点向从节点复制,半数以上达成一致commit,失效节点不一致后,主节点向前迭代寻找一致点,发送后面全部log
3.7 Raft Term
- leader只服务于一个term
- 每个term只有一个leader
- 每个节点存储当前的term
- 每个节点term从一开始只增不减
- 所有rpc的request response携带term
- 只commit本term内的log
3.8 Raft主节点失效
- leader定期发送appendEntries RPCs给其余所有节点
- 如果follower有一段时间没收到AE,则转换成为candidate
- candidate自增自己term,同时使用request试图成为leader
- 其他follower只投票给term比自己高的candidate
3.9 Raft安全性
- 同term
- term内安全性,对于所有已经committed的<term,index>位置上至多只有一条log
- 由于Raft的多数派选举,保证一个term中只有一个leader
- 在任何<term,index>上至多只有一条log:leader写入
- 跨term
- 目标:如果一个log被标记committed,那这个log一定会在未来所有的leader中出现 (leader completeness)
- raft选举检查log是否过期,最新才能当选leader
- 选举需要多数派投票,committed log在多数派中(有overlap)
- 新leader一定持有committed log,且leader永远不会overwrite log
3.10 安全性验证
- TLA+验证
- 形式验证;以数学的形式对算法进行表达,由计算机程序对算法所有状态进行遍历
4. 实现细节与未来
4.1 Raft构建KV系统
- client -> RPC -log-> Raft -commit-> DBEngine
- 需要状态机applied之后才能返回RPC
- 一致性读写
- 写log被commit返回客户端成功;读操作写入log,状态机apply时返回1client 增加log量
- 写log被commit返回客户端成功;读操作先等待所有committed log apply再读取状态机 优化写时延
- 写log被状态机apply,返回给client;读操作直接读状态机 优化读时延
- Raft不能保证一直有一个leader,只保证一个term至多有一个leader;可能存在多个term的leader
- 确定合法的Leadership
- 通过一轮heartbeat确认leadership
- 通过上一次heartbeat时间来保证接下来的有段时间内follower不会timeout;同时follower在这段时间内不进行投票;如果多数follower满足条件那么这段时间内则保证不会有新leader产生
- 取决于Raft实现程度及读写情况决定方案
- 多个副本只有单个可以提供服务:服务无法水平扩展
- 按key分配raft组 分别当不同key range的leader
- 增加更多raft组:如果操作跨raft组
4.2 共识算法研究方向
- Raft关于log
- 当过多log占用后启动snapshot替换掉log
- 对于持久化状态机快速产生snapshot
- 多组raft应用log如何合流
- configuration change
- joint-consensus以及单一节点变更两种方案
- 真实世界中不是所有错误都fail-stop
- cloudflare etcd在partial network下outage很久
4.3 共识算法的未来
- 高性能
- us级别共识,us级别容错
- HovercRaft:P4 programable switch
- Mu:one-side RDMA 不通过RPC直接写follower内存
- 多节点提交 Leaderless
- 节点跨地域
- EPaxos使用冲突图方式允许并行commit
- Raft Paxos相互移植
- 关联两种算法
- 共识算法作为系统
- 多数分布式系统选择共识算法作为底座
- 不同一致性协议有不同特性
- virtual consensus in delos
- 对外暴露一致性log作为接口
- 内部对于log可以选择不同实现
三、个人总结
本节课我学习了分布式一致性协议的相关内容,以及其通过Raft的实现;Paxos算法是非常复杂的算法,在课后学习中还需要加强对此方面的深入理解。