通过我们实现的一致性算法,CockroachDB架构的Replication层在节点之间复制数据并确保这些副本之间的一致性。

如果你还没阅读过架构概览,建议你先阅读该篇。

概览

高可用性要求你的数据库可以容忍节点掉线而不会中断对应用程序的服务。 这意味着需要在节点之间复制数据以确保数据可用。

但是,在节点掉线后保证仍数据的一致性是一项挑战,许多数据库做得并不够好。 为解决此问题,CockroachDB使用一致性算法,要求在提交更改之前,需要法定数量的副本同意对range的任何更改。 3是实现法定数量的最小数字(例如,3个节点中有2个节点同意),所以CockroachDB的高可用性(称为Multi-Active Availability)需要3个节点。

可以容忍的故障数等于(Replication factor - 1)/2。 例如,使用3x复制,可以容忍一个故障; 5x复制,可以容忍2个节点故障,以此类推。 你可以使用Replication Zones控制集群,数据库和表级别的Replication factor 。

但是,当发生故障时,CockroachDB会自动意识到节点已停止响应,并且会重新分配数据;当新节点加入集群时,会自动重新均衡该数据,来确保负载均衡。

与其他层的交互

与CockroachDB中的其他层的关联中,Replication层:

组成

Raft

Raft是一种共识协议 ---一种算法,可确保你的数据安全地存储在多台机器上,并且即使其中一些机器暂时断开连接,这些机器也能正确处理当前状态(those machines agree on the current state)。

Raft将包含range副本的所有节点组织到一个组中,称为Raft Group。 Raft Group中的每个副本是“leader”或“follower”。 由Raft选出并长期的leader负责协调对Raft组的所有写操作。 它会定期检查与follower的连接并保持日志复制。 在失去心跳的情况下,follwers在随机选举超时后成为candidates(候选人)并继续举行新的leader选举。

一旦节点收到其包含的range的BatchRequest,它就会将这些KV操作转换为Raft命令。 这些命令是发给Raft group的leader的 - 这使得Leaseholder和Raft leader最好是同一个节点 - 并写入了Raft日志。

关于Raft的更多信息,请查看 The Secret Lives of Data.

Raft日志

当写入收到法定人数并由Raft组leader提交时,它们将被追加到Raft日志中。 这提供了一组所有replicas认同的有序的命令,这是一致性复制的本质。

由于此日志被视为可序列化的,因此可以重放该日志以将节点从过去状态转换为其当前状态。 此日志还允许临时掉线的节点“捕获”到当前状态,而无需以快照的形式接收现有数据的副本。

Snapshots(快照)

每个副本都可以被“快照”,从特定时间戳复制所有数据(因为MVCC所以是可用的)。可以在重新平衡事件期间将此快照发送到其他节点以加快复制。

加载快照后,节点通过重放从创建快照以来raft组中的日志中的操作来获得最新信息。

Leases(租约)

Raft组中的单个节点充当Leaseholder,它是唯一可以向Raft组leader提供读取或写入建议的节点(这两个操作都是从DistSender接收的BatchRequests)。

在处理读操作时,leaseholders绕过Raft;对于Leaseholder的写操作已经提交了,它们已经达成共识,所以相同数据的第二次达成共识是没有必要的。这样做的好处是不会引起Raft所需的网络往返,大大提高了读取速度(不牺牲一致性)。

CockroachDB试图选出一个也是Raft组leader的Leaseholder,它也可以优化写入速度。

如果没有Leaseholder,则任何接收请求的节点都将尝试成为该range的Leaseholder。 为了防止两个节点获得lease,请求者包括它拥有的最后一个有效lease的副本; 如果另一个节点成为Leaseholder,则忽略其请求。

Co-location with Raft Leadership

range的lease与Raft leader完全分开,因此如果没有进一步操作,Raft leader和Range lease可能不会由同一副本持有。 但是,我们可以通过将同一节点设置为Raft leader和Leaseholder来优化查询性能; 如果收到请求的Leaseholder可以简单地向自己提出Raft命令,而不是将它们传送到另一个节点,它会减少网络往返。

为实现这一目标,每次lease续租或转让也会尝试配置它们(指让一个节点同时成为leaseholder和raft组的leader)。 实际上,这意味着很少会不匹配并且总能快速修正。

基于Epoch的lease(表数据)

为了管理表数据的lease,CockroachDB实现了epochs的概念,它被定义为加入集群的节点和断开集群的节点之间的时期 当节点断开连接时,会认为epoch已更改,并且节点会立即丢失其所有leases。

这种机制使我们可以避免跟踪每个range的lease,从而消除了我们可能产生的大量流量。 相反,我们假设lease在节点失去连接之前不会过期。

基于Expiration(过期)的lease(元数据和系统ranges)

你的表的元数据和系统range(在Distribution层中详细说明)被视为普通kv数据,因此也有lease。 但是,他们没有使用epoch,而是拥有基于到期的lease。 这些lease只是在特定的时间戳(通常是几秒)到期 - 但是,只要节点继续提出Raft命令,它就会继续延长lease的到期时间。 如果没有,则包含尝试读取或写入range的range的副本的下一个节点将成为Leaseholder。

成员变更:rebalance/repair

每当集群的节点数发生变化时,Raft组的成员都会发生变化,为了确保最佳的可用性和性能,需要重新平衡副本。 看起来成员是否变化取决于是否有节点新增或节点下线。

新增节点: 新节点将有关其自身的信息传递给其他节点,表明它有可用空间。 然后,群集将一些副本重新平衡到新节点上。

下线节点: 如果Raft组的成员停止响应,则在5分钟后,群集开始复制被下线的节点在其他节点上的数据,以此来重新平衡。

重新平衡副本

当CockroachDB检测到成员变更时,会在节点之间移动副本。

这是通过使用来自Leaseholder的副本的快照,然后通过gRPC将数据发送到另一个节点来实现的。 传输完成后,具有新副本的节点加入该range的Raft组; 然后,它会检测位于Raft日志中的最新条目中其最新时间戳之后的操作,并重放Raft日志中的操作。

与其他层的交互

Replication & Distribution Layers

Replication层接收来自其节点和DistSender的请求。 如果此节点是range的Leaseholder,则它接受请求; 如果不是,则返回一个错误,指向它认为是Leaseholder的节点,然后将这些KV请求转换为Raft命令。

Replication层将BatchResponses发送回Distribution层的DistSender

Replication & Storage Layers

Committed Raft命令被写入Raft日志,并最终通过Storage层存储在磁盘上。

Leaseholder从其RocksDB实例提供读取,该实例位于Storage层中。

What's Next?

了解CockroachDB如何从磁盘中读取和写入数据: Storage Layer。ß