当前位置: 首页 > 工具软件 > CockroachDB > 使用案例 >

CockroachDB架构-复制层

文建业
2023-12-01

本文知识点来源于官网地址https://www.cockroachlabs.com/docs/v22.1/architecture/replication-layer.html

总览

CRDB体系结构的复制层在节点之间复制数据,并通过我们的共识算法确保这些副本之间的一致性。
高可用性要求数据库能够容忍节点离线,而不会中断对应用程序的服务。通过在节点之间复制数据可以确保数据一直可以被访问。
节点离线时仍然能保障数据的一致性是一个挑战,CRDB使用了一种共识算法,要求在提交更改之前,所有副本的仲裁都同意对range的任何更改。
3是实现quorum的最小数目(即3个中的2个),所以CRDB的高可用性需要至少3个节点。
可容忍的失败数等于 (复制因子- 1)/2 。例如,对于3副本可以容忍一个节点失效;5副本容忍两个节点失效,等等。可以在集群、数据库和表级别控制复制因子。
当发生故障时,CRDB会自动意识到节点停止响应,并努力重新分配数据,以继续最大化生存能力。
当新的节点加入集群时,数据会自动在集群上重新平衡,确保负载均匀分布。

组件

Raft

Raft是一种共识协议——它是一种算法,可以确保数据安全地存储在多台机器上,即使其中一些机器暂时断开,也能就当前状态达成一致。
Raft将包含range副本的所有节点组织成一个组——Raft组。Raft组中的每个副本要么是leader,要么是follower。leader由Raft选出,负责协调所有写入到Raft组的操作。它会定期给follower发送心跳,并保持他们的日志被复制。在没有心跳的情况下,follower在随机选举超时后成为candidate,继续举行新的leader选举。
还引入了第三种副本类型,即 “不投票”副本。这些副本不参与Raft选举,但对于需要低延迟多区域读操作场景非常有用。
一旦节点接收到它所包含range的BatchRequest,它就会将这些KV操作转换为Raft命令。这些命令被提交给Raft leader——并被写入Raft日志。

Raft日志

当写入操作达到quorum数量(如3个中的2个)并由Raft leader提交时,就会被追加到Raft日志。这包含了所有副本一致同意执行的有序命令集。
因为该日志是序列化的,所以可以通过重放日志的方式使节点从过去的状态恢复到当前状态。该日志还允许临时脱机的节点“恢复”到当前状态,而不需要以快照的形式接收现有数据的副本。

“不投票”副本

在V21.1版本以前,CRDB只支持投票副本,所有副本都要作为投票者参与Raft共识协议。但是,需要所有的副本参与到共识算法中,意味着增加复制因子以增加写延迟为代价,因为额外的副本需要参与Raft仲裁。
为了更好地支持多区域集群,比如让多region读取更高效,引入了一种新的副本类型:“不投票”副本。
不投票副本跟随Raft日志(因此能够提供跟随读取),但不参与仲裁。它们对写延迟几乎没有影响
它们有时也被称为只读副本,因为它们只提供读服务,而不参与仲裁(因此不会产生相关的延迟成本)。
非投票副本可以通过num_votersnum_replicas来配置。当num_voters小于num_replicas时,差值代表不投票副本的数量。但是,大多数用户应该使用高级的多区域SQL特性来控制不投票副本位置。

快照

每个副本都可以打“快照”,它将根据特定的时间戳(MVCC功能)复制所有数据。此快照可以在重新平衡期间发送到其他节点,以加快复制。
在加载快照之后,节点通过重放Raft组日志中自快照拍摄以来发生的所有操作来获取最新信息。

租赁

Raft组中的单个节点充当租赁者(以下统称为leaseholder),这是唯一可以向Raft leader提供读操作或提议写操作的节点。
CRDB尝试将leaseholder和Raft leader放在同一个节点,这样可以优化写的速度。
如果没有leaseholder,任何接收到请求的节点都将尝试成为该range的leaseholder。为了防止两个节点获取租约,请求者包含它拥有的最后一个有效租约的副本;如果另一个节点成为leaseholder,它的请求将被忽略。
当提供强一致的读取时,leaseholder绕过Raft;对于首先提交的leaseholder写操作,它们必须已经达成共识,因此没有必要对相同的数据进行第二次共识。这样做的好处是不会产生Raft所要求的网络往返延迟,并大大提高了读取速度(而不牺牲一致性)。

与Raft leader协同工作

range租约完全独立于Raft leader,因此如果没有进一步动作的话,Raft leader和range租约可能不会由同一个副本持有。但是我们可以通过将同一个节点同时设置为Raft leader和leaseholder来优化查询性能;如果接收请求的leaseholder可以简单地向自己提出Raft命令,而不是与另一个节点通信,就可以减少网络往返。
为了实现这一点,每次租约续期或转让都试图对它们进行配置。在实践中,这意味着不匹配是罕见的,而且自我纠正很快。

基于epoch的租约(表数据)

为了管理表数据的租约,CRDB实现了“epoch”的概念,它被定义为节点加入集群和节点从集群断开之间的时间段。为了延长租约,每个节点必须定期更新活动记录,该记录存储在一个系统range的key上。当一个节点断开连接时,它将停止更新活动记录,并且认为epoch已更改。这将导致节点在活动记录过期几秒钟后失去所有租约。
因为租约直到节点断开与集群的连接才到期,所以leaseholder不必单独更新自己的租期。以这种方式将租约与节点活动联系起来,可以让我们消除大量的流量和Raft处理,同时仍然跟踪每个range的租约。

基于过期的租约(元范围和系统range)

meta和系统 range也是正常的key-value数据,因此像表数据一样有租约。
然而,与表数据不同的是,系统range不能使用基于epoch的租约,因为这会创建一个循环依赖:系统range已经被用于为表数据实现基于epoch的租约。因此,系统range使用基于到期的租约代替。基于过期的租约在特定时间戳(通常在几秒钟后)到期。但是,只要节点继续提出Raft命令,它就会继续延长租约。如果没有,则下一个包含该range副本的节点将成为leaseholder,该副本试图从该range读取或写入。

如何从故障节点转移租约

当集群需要访问故障的leaseholder节点上的range时,该range的租约权必须转移到正常的节点。这个过程如下:

  1. 故障节点的活动记录存储在一个系统range内,过期时间为9秒,每4.5秒心跳一次。当节点故障时,集群必须等待记录过期的时间是不同的,但平均为6.75秒。
  2. 正常的节点试图获取租约。这个动作会被拒绝,因为租约获取只能发生在Raft leader节点上,而健康节点还不是leader节点。因此,必须进行Raft选举。
  3. 尝试获取租约的动作会唤醒与租约相关的range。
  4. 接下来会发生什么取决于租约是在表数据上还是在meta range上还是在系统range上:
    (1) 如果租约在meta range或系统range上,解除该range的节点将根据活动记录检查Raft leader是否活动。如果leader不在,它会发起一场竞争试图赢得Raft的领导权,这样它就可以成为leaseholder。
    (2) 如果租约位于表数据上,则跳过上面描述的“leader是否存活?”检查,并立即进行选举。跳过该检查,因为它会对表数据使用的活动记录引入循环依赖,而表数据本身存储在一个系统range中。
  5. 进行Raft选举,从健康的节点中选出新的leader。
  6. 租约获取现在可以由新当选的Raft leader处理。
    这个过程的活动到期时间不超过9秒,加上两次网络往返的成本:1 用于Raft leader选举,2 用于租约获取。
    最后,请注意,上面描述的过程是惰性启动的:它只在对与租约相关的range提出新请求时发生。

leaseholder重新平衡

因为CRDB是从range的leaseholder中读取数据,所以如果跟读最靠近的副本持有租约权,那么性能会更好。但由于到集群的流量是不断变化的,我们希望能动态地转移哪些节点持有租约。
每个leaseholder会定期(大型集群默认每10分钟一次,小型集群则更频繁)判断是否应该将租约转移到另一个副本,主要考虑因素如下:

  1. 每个节点的请求数
  2. 每个节点的租约数
  3. 节点之间的延迟

Intra-locality
如果所有副本都位于同一位置,则完全根据每个包含副本的节点上的租约数量做出决策,试图在所有节点上实现租约的大致公平分布。这意味着分布不是完全平等的;它有意容忍节点之间的小偏差,以防止抖动(例如,为了达到平衡而进行的过度调整)。

Inter-locality
如果副本位于不同的位置,将尝试计算哪个副本将成为最佳的租约者,即提供最低的延迟。
为了实现动态的租约者再平衡,一个区域的当前租约者跟踪它从每个地区收到的请求的数量,作为指数加权移动平均数。这种计算的结果是,最近请求range最多的地区通常被赋予最大的权重。如果另一个位置开始非常频繁地请求该范围,那么这个计算将转移到给第二个区域分配最大的权重。
当检查leaseholder的再平衡机会时,leaseholder将每个请求区域的权重(即最近请求的比例)与每个副本的区域关联起来,通过检查这些区域的相似程度。
然后,leaseholder对比其他副本评估自己的权重和延迟,以确定调整因素。权重之间的差异越大,位置之间的延迟越大,CRDB就越倾向于权重较大的位置的节点。
在检查leaseholder再平衡机会时,当前leaseholder评估每个副本的再平衡权重和权重最大的地方的调整因子。如果转移leaseholder是有益的和可行的,当前的leaseholder将把租约转让给最好的副本。

控制leaseholder再平衡

可以通过kv.allocator.load_based_lease_rebalancing.enabled和kv.allocator.lease_rebalancing_aggressiveness来控制leaseholder的再平衡。注意,根据部署的需要,您可以通过配置复制区域来对租约和副本的位置进行额外的控制。

成员的变化:平衡/修复

每当集群的节点数量发生变化时,Raft组的成员也会发生变化,为了确保最佳的生存能力和性能,副本需要重新平衡。根据成员关系的变化是添加节点还是脱机节点,情况会有所不同。

  1. 添加节点:新节点与其他节点通信关于自身的信息,表明它有可用的空间。然后集群将一些副本重新平衡到新节点上。
  2. 节点脱机:如果Raft组的一个成员停止响应,5分钟后,集群开始重新平衡,将被停机的节点保存的数据复制到其他节点上。
    重新平衡是通过使用来自leaseholder的副本快照,然后通过gRPC将数据发送到另一个节点来实现的。传输完成后,具有新副本的节点加入该范围的Raft组;然后,它检测到它的最新时间戳位于Raft日志中最新条目的后面,并在自己身上重放Raft日志中的所有动作。

基于负载的副本再平衡

除了在节点加入或离开集群时进行再平衡之外,副本还会根据集群内节点之间的相对负载自动进行再平衡。

 类似资料: