TiDB 集群可用性增强 —— TiDB 5.0 的 Joint Consensus 机制介绍

一、旧版本 TiDB 的可用性隐患

1. Before TiDB 2.0

TiDB 集群的可用性详解及 TiKV Label 规划这篇文章中讲到,TiKV 是一个 Multi-Raft 系统,整个 KV map 被切分为了多个数据分片(即 region),每个 region 构成了各自的 Raft Group,触发 region 分裂的默认大小为 96MB,因此集群数据量越大,其中的 Raft Group(即 region)就越多。
基于切分的比较小的 region,再借助 PD 的调度能力,TiDB 实现了灵活的在线扩缩容,以及自动故障转移能力。
图 1 展示了一个 3 实例 3 副本的 TiKV 集群,在扩容了一个新的 TiKV 实例后,在线(Leader 角色被提前切换到其他成员)完成副本迁移的过程:

  1. 在新加入的 TiKV 上生成 region 的第 4 个副本
  2. 以 snapshot + log 的方式向第四个副本复制数据
  3. 待数据追平后,移除被迁移的副本


图 1

Raft 能提供服务的前提之一是大多数副本保持彼此连通,因此在考虑成本和收益了之后,我们往往将副本数设置为 3 或者 5(关于副本数的选择,详见 TiDB 集群的可用性详解及 TiKV Label 规划)。而在上面的调度过程中,出现了长时间的 4 副本的中间状态。在 4 副本状态时,如果发生了将集群对等的划分为两组 2 副本的网络分区故障,由于当前的副本数为 4,它需要 3 副本才能构成大多数,因此网络隔离开的两个分区都没办法选出 Leader 继续提供服务。与 Leader 迁移相比,副本迁移是一个缓慢的过程,当网络分区发生时,很有可能会导致某些 region 不可用,见图 2。


图 2

2. TiDB 2.0 RC5 引入 Raft Learner 支持

因此,从 TiDB 2.0 RC5 版本开始,引入了 Raft Learner 支持,使 Raft 在副本迁移过程中的大部分时间都维持 3 副本的状态,极大的降低了调度带来可用性降低的风险,见图 4。
图 3 展示了该配置项。该配置项在新版本中已经移除,相应的在迁移中使用 Raft Learner 进行复制的功能也改为了硬编码启用。在新版本的 TiDB 中,Learner 的语义转变为了一个持续存在的不参与投票的 Raft 副本,比如 TiFlash 的副本就是 Learner 角色。


图 3

即使运用了 Raft Learner 来降低出现 4 副本中间状态的时间,在最终切换的一瞬间,依然存在一个 4 副本的中间状态,见图 4。对于某一个 region 来说,刚好在网络故障时处于 4 副本中间状态的几率是微乎其微的,但当一个集群足够大,region 足够多的时候,这个概率就没办法忽略不计了。


图 4

二、 Joint Consensus —— 一种两阶段 Raft 成员变更技术

1. TiDB 5.0 引入 Joint Consensus 支持


图 5

如图 5 所示,假设 Raft 要完成 {a,b,c} → {a,b,d} 的副本迁移。

  • 5.0 版本之前的成员集合转换大致过程:
    1.{a,b,c}
    2.{a,b,c,d[learner]}
    3.{a,b,c,d} ← 4 副本状态
    4.{a,b,d}

  • 5.0 引入 Joint Consensus 状态后的成员集合转换大致过程:
    1.{a,b,c}
    2.{a,b,c,d[learner]}
    3.{a,b,c},{a,b,d} ← Joint Consensus 状态
    4.{a,b,d}

在引入 Joint Consensus 之前成员变更是单步变更,存在 4 副本的危险状态。在 Joint Consensus 状态下,需要满足如下条件:

  • 进入 Joint 状态本身就是一条 log entry;解除 Joint 状态也是一条 log entry
  • 进入 Joint 状态需要满足前状态 {a,b,c} 的 majority
  • 在 Joint 状态中,选举和 log entry commit 都需满足前状态 {a,b,c} 和后状态 {a,b,d} 的 majorities
  • 如果在 Joint 状态中丢失了 Leader 但仍能满足前后状态的 majorities,就可以选出新的 Leader
  • 解除 Joint 状态需要满足前状态 {a,b,c} 和后状态 {a,b,d} 的 majorities

2. Joint Consensus 应对的场景

假设要完成 {a,b,c} → {a,b,d} 的调度

  1. {a,b,c}
  2. {a,b,c,d[learner]}
  3. {a,b,c},{a,b,d} ← Joint Consensus(Joint 状态被成员落实,但解除 Joint 状态的 log 尚未落实)
  4. {a,b,d}

Joint 状态需要同时满足前状态与后状态的 majorities,在 Label 间(图 6、图7)或 Label 内(图 8)发生一个网络隔离的时候,由于这几种网络隔离均能达成某一个隔离区同时满足前状态与后状态的 majorities,因此即使丢失了 leader,也可以通过选举,选出新的 leader。因此副本调度可以安全的推进至 {a,b,d} 的最终状态。


图 6

图 7

图 8

3. Joint Consensus 不能应对的场景

假设要完成 {a,b,c} → {a,b,d} 的调度

  1. {a,b,c}
  2. {a,b,c,d[learner]}
  3. {a,b,c},{a,b,d} ← Joint Consensus
  4. {a,b,d}

当发生如图 9 所示的不单纯的网络隔离的时候,由于两个隔离区都无法满足前状态和后状态的 majority,因此无法选举 Leader 继续服务。


图 9

判断标准:网络隔离后,是否有一个隔离区的副本可以构成同时满足前状态与后状态的 majorities。
有,就可以完成自动故障转移,继续提供服务。
没有,就无法继续服务。

在现实情况中,应根据基础设施的物理位置,合理的规划 TiKV 的 Label,来避免单纯的网络隔离引起不单纯 Label 隔离状况的发生。 Label 规划请参考TiDB 集群的可用性详解及 TiKV Label 规划

4赞