作者:张博康
随着传统企业对业务连续性和数据可靠性的要求不断提高,数据库系统需要同时具备更强的容灾和高可用能力。同城双中心环境下部署成为许多企业实现数据高可用和快速灾难恢复的重要策略。然而,传统的数据库复制方案在双中心部署下存在一定的技术限制,难以同时满足数据一致性和系统可用性。
为了克服这些挑战,TiDB 引入了 DR Auto-Sync(Data Replication Auto Synchronous)自适应同步复制方案,专为同城双中心部署设计,旨在在保障数据一致性的同时提高系统的可用性。
本文将深入探讨 DR Auto-Sync 方案的原理、架构以及其关键技术——Raft Commit Group。
Raft
Raft 是一种分布式共识算法,在 TiDB 集群的多种组件中,PD 和 TiKV 都通过 Raft 实现了数据的容灾。Raft 的灾难恢复能力通过如下机制实现:
- Raft 成员的本质是日志复制和状态机。Raft 成员之间通过复制日志来实现数据同步;Raft 成员在不同条件下切换自己的成员状态,其目标是选出 leader 以提供对外服务;
- Raft 是一个表决系统,它遵循多数派协议,在一个 Raft Group 中,某成员获得大多数投票,它的成员状态就会转变为 leader。也就是说,当一个 Raft Group 还保有大多数节点(majority)时,它就能够选出 leader 以提供对外服务。
副本数目的选择
Raft 算法本身并没有限制一个 Raft 组的成员(即副本)的数目,这个成员数可以为任意正整数,当成员数为 n 的时候,一个 Raft 组的可靠性如下:
- 若 n 为奇数,该 Raft 组可以容忍 (n-1)/2 个成员同时发生故障;
- 若 n 为偶数,该 Raft 组可以容忍 n/2 -1 个成员同时发生故障。
在一般情况下,通常将 Raft 组的成员数设置为奇数,其原因如下:
- 避免造成存储空间的浪费:三成员可以容忍 1 成员故障,增加 1 个成员变为 4 成员后,也只能容忍 1 成员故障,容灾能力维持不变;
- 当成员数为偶数时,如果发生了一个网络隔离,刚好将隔离开的两侧的成员数划分为两个 n/2 成员的话,由于两侧都得不到大多数成员,因此都无法选出 leader 提供服务,这个网络隔离将直接导致整体的服务不可用;
- 当成员数为奇数时,如果发生了一个网络隔离,网络隔离的两侧中总有一侧能分到大多数的成员,可以选出 leader 继续提供服务。
原生 Raft 的限制
遵循 Raft 可靠性的特点,放到现实场景中:
- 想克服任意 1 台服务器的故障,应至少提供 3 台服务器;
- 想克服任意 1 个机柜的故障,应至少提供 3 个机柜;
- 想克服任意 1 个数据中心(机房)的故障,应至少提供 3 个数据中心;
- 想应对任意 1 个城市的灾难场景,应至少规划 3 个城市用于部署。
可见,原生的 Raft 协议对于偶数可用区(AZ,Available Zone)的支持并不友好,3 可用区或许是最适合部署 Raft 的高可用及容灾方案。而在现实情况中,大多数部署环境很少具备同城 3 可用区的条件。比如数字化程度非常高的银行业,基于传统单机系统点对点复制的特性,有些金融机构因客观条件限制,只具备同城 2 可用区,两地 3 可用区的基础设施。
在同城双可用区部署原生的 Raft,主、备可用区按 2:1 的比例来分配 3 个成员,由于网络延迟的差异,灾备可用区会存在异步复制的成员。当主可用区由于故障无法恢复时,灾备可用区仅剩的 1 个成员是无法保障 CAP 中的一致性的。
我们对 Raft 做了一些功能上的扩展,基于这个扩展它可以在一定程度上克服主可用区故障后使用灾备可用区难以恢复一致性数据的难题。
DR(Disaster Recovery),代指同城容灾数据中心,该方案用于应对上文所描述的 TiDB 同城双中心部署,或同城双中心+1 个投票中心部署的需求。
DR Auto-Sync 是一种跨同城(网络延迟<1.5ms)两中心部署的单一集群方案,即两种数据中心只部署一个 TiDB 集群,两中心间的数据同步通过集群自身内部(Raft 协议)完成。两中心可同时对外进行读写服务,任意中心发生故障不影响数据一致性。
为了解决 Raft 天生对于双中心支持的不友好,我们对 TiKV 中的 Raft 以及 PD 的调度能力都做出了大幅加强。
如图所示,为了获得网络发生隔离后的自动恢复能力,本方案采用奇数副本进行部署,这里以 3 副本进行举例,TiKV 和 PD 的 3 个副本都按照主备机房 2:1 的比例进行资源分配,而 TiDB 可以在两中心部署任意多个来获得两中心双活的能力。
从图中可以看到,本方案与原生 Raft 方案不同的是,TiKV 上增加了 Primary/DR 的位置属性,用来让 PD 感知到各个 TiKV 所在的机房信息。同时对 TiKV 中的 Raft 做出了加强,使其根据 PD 下发的同步状态要求,自动地在同步复制和异步复制之间转换。
同步状态
我们定义了三种状态来控制和标示集群的同步状态,该状态约束了 TiKV 的复制方式:
- sync:同步复制,此时 DR 与 Primary 至少有一个节点与 Primary 同步,Raft 保证每条 log 按 label 同步复制到 DR;
- async:异步复制,此时不保证 DR 与 Primary 完全同步,Raft 使用经典的 majority 方式复制 log;
- sync-recover:恢复同步,此时不保证 DR 与 Primary 完全同步,Raft 逐步切换成 label 复制,切换成功后汇报给 PD。
状态转换
简单来讲,集群的复制模式可以自动在三种状态之间自适应的切换:
- 当集群一切正常时,会进入同步复制模式来最大化的保障灾备机房的数据完整性;
- 当机房间网络断连或灾备机房发生整体故障时,在经过一段提前设置好的保护窗口之后,集群会进入异步复制状态,来保障业务的可用性;
- 当网络重连或灾备机房整体恢复之后,灾备机房的 TiKV 会重新加入到集群,逐步同步数据并最终转为同步复制模式;
- 当主机房整体故障且无法恢复时,使用灾备机房的副本重建集群来恢复一致性的数据。
状态转换的细节过程如下:
初始化
集群在第一次启动时是 sync 状态,PD 会下发信息给 TiKV,所有 TiKV 会严格按照 sync 模式的要求进行工作。
同步切异步
1. PD 通过定时检查 TiKV 的心跳信息来判断 TiKV 是否宕机 / 断连;
2. 如果宕机数超过 Primary/DR 各自副本的数量,意味着无法完成同步复制了,需要切换状态;
3. PD 将 async 状态下发到所有 TiKV;
4. TiKV 的复制方式由双中心同步方式转为原生的 Raft 多数派提交方式。
异步切同步
1. PD 通过定时检查 TiKV 的心跳信息来判断 TiKV 是否恢复连接;
2. 如果宕机数小于 Primary/DR 各自副本的数量,意味着可以切回同步了;
3. PD 将 sync-recover 状态下发给所有 TiKV;
4. TiKV 的所有 region 逐步切换成双机房同步复制模式,切换成功后状态通过心跳同步信息给 PD;
5. PD 记录 TiKV 上 region 的状态并统计恢复进度:
A) 所有 Regoin 都恢复后,PD 将状态切换为 sync,将 sync 状态下发给所有 TiKV;
B) 如果在过程中又发生宕机,执行同步切异步流程。
PD 在整个过程中扮演了极为重要的角色,在双中心同步复制方案中几乎所有的配置都在 PD 中完成,包括 Label 设置,Primary/DR 角色配置,阻塞窗口 wait-store-timeout 的配置等。为了有效的在分布式系统中表示当前的同步状态,PD 在向 TiKV 下发状态的同时会绕过原生的 Raft 通过一个新的接口将当前的复制模式同步的写入所有的 PD 磁盘中,这是为了当 PD leader 出现宕机时,用户可以准确的获取当前的复制模式。
RPO & RTO
RPO
在正常情况同步复制时,主机房故障后,灾备机房有跟主机房同样新的数据,通过可继续提供服务,RPO 为 0。
当同步复制转异步复制后降级提供服务时,发生主机房整体故障的特殊情况,RPO 不为 0。
RTO 在不同的场景下的计算方法
- 两机房网络断开时长小于 wait-store-timeout 所设置的时间时,RTO 为网络断开时间和 30s(基于默认的 Raft 心跳设置)中更大的那个;
- 两机房网络断开或灾备机房整体宕机时,RTO 为阻塞窗口 wait-store-timeout 所设置的时间;
- 主机房整体宕机时,RTO 为报警响应时间 + 重建 PD 操作时间(熟练 DBA 分钟级) + 恢复 TiKV 单副本时间(熟练 DBA 分钟级)+ 集群验证时间(用于验证数据库可用,应用连接顺畅),恢复后的单副本集群可以直接提供服务,后续的扩容以及扩副本操作可以在线执行。
为了解决分布式共识算法天生在双可用区(AZ)部署时容灾能力的缺陷,我们对分布式共识算法做了加强。
为了获得发生网络隔离故障后的自动恢复能力,需要选用奇数个成员,我们以双可用区部署 7 成员的 Raft 为例来说明这个技术方案。
如图所示,0、1、2、5 成员在一个可用区,剩下的在另一可用区。一般的共识算法通过大多数成员(majority)来实现唯一修改提交并且容忍少数副本(minority)丢失。但是在跨双可用区的情况下,奇数成员的 majority 不一定能容忍可用区损坏。写入只同步在了0、1、2、5 这些成员时,此时足够形成 majority 提交写入。但若之后该可用区损坏,另一可用区上没有该写入,那么就造成了数据丢失。
核心技术 1 —— Raft commit group
与原生 Raft 不同,我们给 Raft 成员增加了提交组(commit group)的概念。每个成员会分配在最多一个组(group)里。在进行日志提交时,除了要满足多数派的前提,还需要满足日志复制到至少两个不同的组里。多数派的前提保证了正确性;至少两个不同组保证了数据的安全。
提交组是可以运行时动态开关的。当要进行同步复制时,开启提交组,保证数据安全;当要切换成异步复制时,关闭提交组,保证可用性。而提交组的划分与 Raft 解偶,由上层指定。在本方案里,划分方式根据地理位置属性决定。
如图所示,0、1、2、5 可以凑成大多数成员(majority),但是一旦 AZ 0 整个可用区损坏以后,最新的修改也将丢失。提交组将成员按照一定的规则给组织起来,分成不同的组。并且规定修改必须满足大多数成员落实,且复制到至少两个组才能提交。在这种情况下,上述 0、1、2、5 就不是一个符合要求的成员组合。虽然这个组合能满足大多数成员落实,但这个组合只包含了一个组 AZ 0。
如图所示,图中的 0、1、2、6 就是一个合格的组合。它一共包含 4 个成员,满足了大多数成员落实的要求;这四个成员分布在两个不同的组 AZ 0、AZ 1 中,因此它是一个符合要求的组合。
核心技术 2 —— 保障同步复制的请求阻塞窗口
阻塞窗口用于在发生双可用区通信故障时,在一个预先设置的时间内,通过阻塞新请求的提交来保持双可用区同步复制模式。
- 当阻塞窗口设置为 0 时,两个可用区将保持在异步复制状态;
- 当阻塞窗口设置为 1 分钟时,网络断连或灾备可用区发生整体故障的最初 1 分钟内,主可用区会自动阻塞新的请求,来保障双可用区复制的同步性;
- 当阻塞窗口设置为一个足够大的时间时(如 1 年),网络断连或灾备可用区发生整体故障的 1 年内,都会阻塞新的请求,在实际使用中可以认为双可用区处于持久的同步复制状态。
核心技术 3 —— 自适应复制状态切换
如图所示,双可用区部署的 Raft 基于这个状态机设计在同步复制和异步复制之间完成自动转换。
1. 同步状态说明
我们定义了三种状态来控制和标示集群的同步状态,该状态约束了 TiKV 的复制方式:
- 同步复制状态:即图中“双可用区同步模式”,此时 Raft 在提交组(commit group)模式下工作,以确保灾备可用区具备同步(RPO=0)的数据;
- 异步复制状态:即图中“大多数成员模式”,此时采用经典的 Raft 大多数成员(majority )模式做复制,不保证灾备可用区具备同步的数据;
- 恢复同步状态:即图中“恢复同步模式”,此时逐步追平数据,追数过程中不保证灾备可用区具备同步的数据。
2. 状态转换说明
复制模式可以自动在三种状态之间自适应的切换:
- 当集群一切正常时,会转换为同步复制状态来最大化保障灾备可用区的数据完整性;
- 当两个可用区间的网络断连或灾备可用区发生整体故障时,在经过一段提前设置好的阻塞窗口之后,集群会进入异步复制状态,来保障业务的可用性
- 当网络重连或灾备可用区整体恢复之后,灾备可用区的成员会重新加入到集群,逐步同步数据并最终转为同步复制模式;
- 当主可用区整体故障且无法恢复时,使用灾备可用区的成员来恢复一致性的数据。
这种部署方式确保了数据在两个中心的合理分布,为实现同步复制奠定了基础。
核心技术 4 —— 少数派灾难恢复
前面我们知道了通过 Raft commit group 可以保证在灾备可用区至少有一个副本有最新的数据。当主可用区整体故障且无法恢复时,灾备可用区的剩余副本就可以继续提供一致性的服务。但有个问题是,灾备可用区只有少数派副本,按照 Raft 的多数派规则无法选出 Leader。
此时我们引入少数派灾难恢复的机制,通过 PD 收集灾备可用区上的所有副本信息,判断出来 Region 的哪个副本有最新数据,然后绕过 Raft 规则手动指派其为 Leader,并执行 Conf Change 将成员列表中主可用区的副本变成 Learner。Learner 不参与多数派的投票,那么之后灾备可用区的这些副本也能形成多数派正常提供服务。
如下图以三副本为例,对于该 Region 假设 Peer 2 和 Peer 3 在主可用区,Peer 1 在灾备可用区。当主可用区故障后,Peer 1 和 Peer 2 不可用,从 Peer 1 的视角看,整个 Raft Group 有三个成员,此时无法形成多数派选举处 Leader。当手动指定了 Peer1 为 Leader 后,它就可以执行 Conf Change 将 Peer 2 和 Peer 3 变成 Learner,那么 Peer 1 自己即是多数派,后面能正常的提供服务。
除了同城两中心,还有同城三中心和两地三中心等部署方案。下面我们来看下各种方案的优缺点:
同城双中心方案(DR Auto-Sync)
适用场景
- 同城只有双中心;
- 网络延迟低且稳定的环境(两个数据中心距离在 50 km 以内,通常位于同一个城市或两个相邻城市,数据中心间的网络连接延迟小于 1.5 ms,且网络质量可保证无频繁丢包)。
优点
- 具备同城双数据中心高可用和容灾能力;
- 当处于同步复制状态(sync)的集群发生单 IDC 不可用时,可进行 RPO = 0 的数据恢复;
- 如果从数据中心发生故障,丢失了少数 Voter 副本,能自动切换成 async 异步复制模式,此时主中心业务访问不受影响;
- 少数 Voter 副本发生异常不可访问,可自动发起 leader election,并在 20s 内自动恢复服务;
- DR DC Voter 可以根据实际情况驱逐 region leader,避免 leader 跨数据中心访问,主要控制 PD 的 Leader 节点所在的 IDC。
缺点
- 运维管理和故障场景处理相对复杂;
- 跨 region 读写数据场景极端场景可能出现 ACID 保障不了,需要借助应用层对账措施双重保障;
- 当不处于同步复制状态(sync)的集群发生了灾难,DR 不能保证满足 RPO = 0 进行数据恢复;
- 如果主数据中心发生故障,丢失了大多数 Voter 副本,但是从数据中心有完整的数据,可在从数据中心恢复数据,此时需要人工介入,通过专业工具进行内部一致性校验,判断是否存在不一致的情况,并对数据进行一致性恢复(故障时间段数据更新越多,校验时间越久,业务高峰期异常和业务低峰期异常的耗时不同),从数据中心对 2 个副本进行角色重新定义,并继续提供读写服务;
- Primary IDC 故障后,DR IDC 的 PD 集群需要重建。
同城三中心方案
同城三中心方案,即同城三个机房部署 TiDB 集群,三数据中心间的数据同步通过集群内部 Raft 协议完成。三数据中心可同时对外进行读写服务,任意中心发生故障不影响数据一致性。集群 TiDB、TiKV 和 PD 组件分别同时分布在三个数据中心,这是高可用性最优的方案,也是 TiDB 优先推荐的方案。
适用场景
- 同城三中心;
- 网络延迟低且稳定的环境。
优点
- 所有数据的副本分布在三个数据中心,同时具备高可用和容灾能力,且故障发生时能自动切换;
- 任何一个数据中心失效后,不会发生任何数据丢失(RPO=0);
- 任何一个数据中心失效后,其它两个数据中心会自动发起 leader election,并在 20s 以内自动恢复对外服务。
缺点
对跨机房间网络稳定性要求较高,性能会受网络延迟影响,具体影响如下:
- 对于写入的场景,由于多节点间基于 Raft 协议同步数据,所有写入的数据需要同步复制到集群内至少 2 个数据中心,TiDB 写入过程使用两阶段提交,故写入延迟至少 2 倍数据中心间的延迟;
- 对于读请求来说,如果数据 leader 与发起读取的 TiDB 节点不在同一个数据中心,如果此时网络不稳定,读请求的效率也会受网络延迟影响;
- TiDB 中的每个事务都需要向 PD leader 获取 TSO,当 TiDB 与 PD leader 不在同一个数据中心时,由于每个写入的事务请求会获取两次 TSO,所以 TiDB 实例上运行的事务性能会受网络延迟的影响。
两地三中心方案
两地三中心架构,即生产数据中心、同城灾备中心、异地灾备中心的高可用及容灾方案。在这种模式下,两个城市的三个数据中心互联互通,如果一个数据中心发生故障或灾难,其它数据中心可以正常运行并对全部业务实现快速接管,保障业务连续性。
适用场景
- 同城双中心+异地中心;
- 网络延迟低且稳定的环境。
优点
- 异地跨城级高可用能力,可以应对异地城市级自然灾害;
- 任何一个数据中心失效后,不会产生任何数据丢失 (RPO = 0)(异地跨城 IDC 的网络延迟较大,异地 IDC 的数据会有所丢失);
- 任何一个数据中心失效后,其他两个数据中心会自动发起 leader election,并在 20s 自动恢复服务。
缺点
同城两数据中心城市灾难,集群不可用且异地机房数据恢复不保证 RPO = 0,性能受网络延迟影响。具体影响如下:
- 对于写入的场景,所有写入的数据需要同步复制到至少 2 个数据中心,由于 TiDB 写入过程使用两阶段提交,故写入延迟至少需要 2 倍数据中心间的延迟;
- 对于读请求来说,如果数据 leader 与发起读取的 TiDB 节点不在同一个数据中心,也会受网络延迟影响;
- TiDB 中的每个事务都需要向 PD leader 获取 TSO,当 TiDB 与 PD leader 不在同一个数据中心时,它上面运行的事务也会因此受网络延迟影响,每个有写入的事务会获取两次 TSO。