深入解析 Raft 模块在云溪数据库中的优化改造(上)

Raft 简介

Raft 作为一种管理日志复制的分布式一致性算法,由斯坦福大学的 Diego Ongaro 和 John Ousterhout 在论文中提出。在 Raft 出现之前,Paxos 一直是分布式一致性算法的标准。但 Paxos 相对难以理解,Raft 的设计目标就是简化 Paxos,使得一致性算法更容易理解和实现。

Paxos 和 Raft 都是分布式一致性算法,其过程如同投票选举领袖(Leader),参选者(Candidate)需要说服大多数投票者(Follower)给他投票,一旦选举出领袖,就由领袖发号施令。Paxos 和 Raft 的区别在于选举的具体过程不同,社区中关于 Raft 算法的详细讲解非常丰富,这里就不再赘述。

云溪数据库中的 Raft 算法

云溪数据库是分布式数据库,与 OceanBase、CockroachDB、TiDB 一样都是NewSQL 家族的一员。云溪数据库具备强一致、高可用的分布式架构,能够水平扩展,提供企业级的安全特性,完全兼容 PostgreSQL 协议,能够为用户提供完整的分布式数据库解决方案。云溪数据库的整体架构如图 1 所示:

深入解析 Raft 模块在云溪数据库中的优化改造(上)_第1张图片

图1:云溪数据库的整体架构

云溪数据库各方面的强一致性都通过 Raft 算法实现。首先,Raft 算法保证分布式多副本之间数据强一致性以及外部读写的一致性。简而言之,云溪数据库中数据会有多个副本,这些副本存放在不同的机器上,当其中一台机器故障宕机后,数据库依旧能够对外提供服务。此外,云溪数据库会根据插入数据的键,将数据划分为多个 Range,每个 Range 上的数据均由一个 Raft Group 来维持多个副本之间数据的一致性。因此,准确地说云溪数据库使用的是 Multi-Raft 算法。

具体来说,云溪数据库的存储层基于 RocksDB 开发,利用单机的 RocksDB,云溪数据库可以将数据快速地存储在磁盘上;在出现单机故障时,利用 Raft 算法可以快速地将数据复制到机器上。在这个过程中,数据的写入是通过 Raft 算法接口实现的,而不是直接写入 RocksDB。通过 Raft 算法,云溪数据库变成了一个分布式的键值存储系统,面对不超过集群半数的机器故障情况宕机,完全能够通过 Raft 算法自动把副本补全,做到业务对故障的无感知。

在项目开发前期,云溪数据库中的 Raft 算法采用的是开源的 etcd-raft 模块,该模块主要提供如下几点功能:

· Leader 选举;
· 成员变更,可以细分为:增加节点、删除节点、Leader 转移等;
· 日志复制。

云溪数据库利用 etcd-raft 模块进行数据复制,每条数据操作都最终转化成一条 RaftLog,通过 RaftLog 复制功能,将数据操作安全可靠地同步到 Raft Group 中的每一个节点上。不过在实际操作中,根据 Raft 的协议,只需要同步复制到多数节点,即可安全地认为数据写入成功。

但是在后续的生产实践中,云溪数据库研发团队逐渐发现 etcd-raft 的模块仍存在诸多限制,于是陆续开展了如下多个方面的优化工作,具体包括:

· 新增 Raft 角色
· 新增 Leader 亲和选举
· 混合序列化
· Raft Log 分离与定制存储
· Raft 心跳与数据分离

下面将着重介绍第一点,即云溪数据库团队根据业务需要为 Raft 模块新增的三种角色。

云溪数据库对 Raft 模块的改进
新增 Raft 角色

1、强同步角色

为解决部署在跨地域的多数据中心数据同步问题,达到数据在多地共同写入的效果,实现地域级别的容灾能力,云溪数据库研发团队在 etcd-raft 模块中新增了强同步角色。

具体措施如下:

为副本增加强同步标识以及配置和取消强同步标识的逻辑。
etcd-raft 模块原有的日志提交策略:Leader 得到超过半数副本(包括 Leader 自身)的投票才能提交 Raft 日志。云溪数据库在原有的过半数提交策略基础上,增加了强同步角色的日志提交策略——日志提交还需要得到所有强同步副本的投票。
为强同步角色设计了如图 2 所示的故障识别与处理机制:通过心跳超时机制识别强同步故障,提交日志时忽略故障的强同步角色,并将故障信息录入数据库日志并告知用户。
为强同步角色的心跳超时 时间增加热更新功能。

深入解析 Raft 模块在云溪数据库中的优化改造(上)_第2张图片

图2 强同步角色的处理逻辑

经过以上四点改造后,etcd-raft 模块新增强同步角色,能够实现如下功能:

· 允许用户为指定副本配置或取消强同步角色,不影响强同步角色所在副本的原有特性。例如对全能型副本配置强同步角色后,该副本依然存储 Raft 日志和用户数据,参与投票,参与 Leader 选举,当选为 Leader 可提供读写服务,Follower 时可提供非一致性读。
· 强同步角色的数据与 leader 保持同步。
· 允许用户配置强同步角色的心跳超时 时间。如果强同步角色发生故障,Raft 集群将在该超时 时间后恢复写入功能,故障期间的写入也会在超时 时间后提交。Raft会在识别到强同步角色故障后暂时取消强同步标记,并在该强同步角色故障解决后自动恢复。强同步角色故障和恢复的信息对用户可见。
· 允许用户在 SQL 终端查询强同步角色的配置状态。

2、只读型角色

由于云溪数据库具备 HTAP 的特性,因此需要在 Raft Group 中增加一种相对独立的特殊副本,对外仅提供读服务(例如将该类型副本的存储引擎替换成列存引擎)以实现 OLAP 的功能。为了在 Raft Group 中增加这种特殊副本,同时不影响原有的集群特性,云溪数据库研发团队在 Raft 中设计了一种新的只读型角色。

具体实现措施如下:

  1. 增加只读型角色标识,增加只读型角色的创建、删除、重平衡逻辑。
  2. 增加只读型副本接收到请求后读取数据的逻辑:如果只读型角色的时间戳不小于请求的时间戳,则提供读服务;否则会重试多次,重试达到限制后会将读取超时错误返回到 SQL 终端。
  3. 为只读型副本的时间戳更新间隔、读取时的最大重试次数等参数增加热更新功能。

etcd-raft 模块新增只读型角色后,能够实现如下功能:

· 允许用户在指定位置创建、删除或移动只读型角色。只读型角色支持基于负载均衡的重平衡功能,移动到压力相对较小的节点。
· 该角色对外仅提供读服务,存储 Raft 日志和用户数据,不参与投票,不参与 Leader 选举,可提供读服务。
· 允许用户配置只读型副本的时间戳更新间隔、读取时的最大重试次数,可以用于对只读型副本的读取性能进行调优。
· 允许用户在 SQL 终端查询只读型角色的配置情况。

3、日志型角色

云溪数据库不支持在两数据中心三副本部署模式下提供双活模式,无论如何部署副本的位置,总有一个数据中心拥有过半数的副本。当拥有过半数副本的数据中心故障时,另一个数据中心由于所拥有的可用副本数不满足过半数,会导致云溪数据库无法对外正常提供服务。为解决此类问题,提高云溪数据库的容灾能力,同时充分利用和整合资源,避免出现资源闲置造成的浪费现象,提升双活数据中心的服务能力,项目团队在 etcd-raft 模块增加了日志型角色。

具体实现措施如下:

  1. 日志型副本参与 Leader 选举,拥有投票权,并且可以成为 Leader。在普通副本缺少最新日志的故障场景下,为了恢复集群的可用性,需要日志型副本当选为 Leader,并向其他副本追加日志,使得该副本拥有最新的日志。然后发起 Leader 转移,拥有最新日志的副本当选为 Leader 和 Leaseholder,完成集群恢复。
  2. 日志型副本不能发送快照。由于日志型副本不含用户数据,若发送快照将导致其他副本丢失数据,因此禁止日志型副本发送快照。
  3. 日志型副本不能成为 LeaseHolder。禁止从日志型副本读取数据,且在日志型副本成为Leader 的情况下,在其他副本拥有最新日志后,将立即转移 Leader 到该副本上。
  4. 日志型副本保留日志。日志型副本的日志可用于故障恢复,因此延长其日志保留时间。原有的日志清理策略为当可清理的日志 index 数量大于等于 100 或实际大小大于等于 64KB 时,执行日志清理操作。当出现节点宕机时,待清理的日志超过 4MB 执行日志清理操作。日志型副本的日志清理策略为:将日志清理请求按小时打包、延迟处理,默认清理时间值为24,即将日志清理请求延迟24小时处理,达到日志保留的效果。用户可配置清理时间值,可配置范围是[-1, MaxInt],若配置为 -1 则表示不保留日志,按照原来的逻辑执行清理操作。
  5. 日志型副本异地重启。日志型副本异地重启时会因尝试提交心跳消息携带的 Commit 值而宕机。修改 follower 处理心跳的逻辑,如果日志型 follower 收到心跳消息的 Commit 值比实际的 lastIndex 值大,就将心跳回复消息的 Reject 字段置为 true、RejectHint 字段置为实际的 lastIndex。Leader 收到 Reject 为 true 的心跳回复消息时,将对应 follower 副本 progress 的 Match 和 Next 更新为实际值,并向该副本追加日志,将其丢失的日志补全。

针对日志型角色增加 Logonly 语法支持,使用 Alter 语句配置样例如下,表 t 拥有 3 个副本,其中将 2 个全能型副本放在北京和济南,日志型副本放在天津:

ALTER TABLE t CONFIGURE ZONE USING num_replicas=2, num_logonlys=1, constraints=’{"+region=beijing": 1,"+region=jinan": 1}’, logonly_constraints=’{"+region=tianjin":1}’;

以两中心三副本(一个全能型、一个强同步、一个日志型)模式进行部署为例,全能型副本与强同步副本分别存放在 DC-1 与 DC-2 的高配机器(或多数机器)中,日志型副本存放在 DC-1 或 DC-2 的低配机器(或少量机器)中,日志增量复制到另一个数据中心的低配机器(或少量机器)。若遭遇数据中心级别的故障,在失去两个副本(一个全能型、一个日志型)后,在另一个数据中心手动启动存放日志型副本的节点,该日志型副本含有基于增量复制得到的日志数据。

深入解析 Raft 模块在云溪数据库中的优化改造(上)_第3张图片

遭遇数据中心级别的故障时的容灾处理(Node7 的日志型副本需要手动重启)

通过给 Raft 算法增加 3 种新的角色,云溪数据库在跨地域集群容灾、支持 OLAP 等方面的能力得到了显著加强。

你可能感兴趣的