复制

由于对象存储中的每个副本都独立运行,并且客户端通常只需要简单多数节点响应即可认为操作成功,因此瞬态故障(如网络分区)可能很快导致副本发散。这些差异最终由异步的、点对点的复制器进程协调。复制器进程遍历其本地文件系统,并以平衡物理磁盘负载的方式并发执行操作。

复制使用推送模型,记录和文件通常仅从本地复制到远程副本。这一点很重要,因为节点上的数据可能不属于那里(例如,在交接和环变更的情况下),并且复制器无法知道应该从集群中的其他位置拉取哪些数据。任何包含数据的节点都必须确保数据到达其应有的位置。环负责副本放置。

为了复制删除操作以及创建操作,系统中删除的每个记录或文件都将标记为墓碑(tombstone)。复制过程在被称为 一致性 窗口 的时间段后清理墓碑。该窗口定义了复制的持续时间以及瞬态故障可以使节点从集群中移除多长时间。墓碑清理必须与复制相关联才能达到副本收敛。

如果复制器检测到远程驱动器已故障,复制器将使用环的 get_more_nodes 接口选择另一个要同步的节点。复制器可以在磁盘故障期间维持期望的复制级别,尽管某些副本可能不在立即可用的位置。

注意

复制器在发生整个节点故障等故障时,不会维持期望的复制级别;大多数故障都是瞬态的。

主要的复制类型是

  • 数据库复制

    复制容器和对象。

  • 对象复制

    复制对象数据。

数据库复制

数据库复制完成低成本的哈希比较,以确定两个副本是否已经匹配。通常,此检查可以快速验证系统中大多数数据库是否已经同步。如果哈希值不同,复制器通过共享自上次同步点以来添加的记录来同步数据库。

此同步点是一个高水位线,它记录了两个数据库已知同步的最后一个记录,并存储在每个数据库中,作为远程数据库 ID 和记录 ID 的元组。数据库 ID 在数据库的所有副本中都是唯一的,而记录 ID 是单调递增的整数。在将所有新记录推送到远程数据库后,本地数据库的整个同步表将被推送,以便远程数据库可以保证它与本地数据库之前同步的所有内容同步。

如果副本丢失,整个本地数据库文件将使用 rsync(1) 传输给对等方,并分配一个新的唯一 ID。

在实践中,数据库复制可以处理每并发设置每秒数百个数据库(最多可用 CPU 或磁盘的数量),并且受必须执行的数据库事务数量的限制。

对象复制

对象复制的初始实现执行 rsync 以将数据从本地分区推送到所有预期驻留的远程服务器。虽然这在小规模上有效,但一旦目录结构无法保存在 RAM 中,复制时间就会急剧增加。此方案被修改为将每个后缀目录的内容哈希值保存到每个分区的哈希文件中。当后缀目录的内容被修改时,该后缀目录的哈希值将不再有效。

对象复制过程读取哈希文件并计算任何失效的哈希值。然后,它将哈希值传输到应该持有分区的每个远程服务器,并且只有在远程服务器上哈希值不同的后缀目录才会被 rsynced。在将文件推送到远程服务器后,复制过程会通知它重新计算 rsynced 后缀目录的哈希值。

对象复制必须遍历的未缓存目录数量,通常是由于失效的后缀目录哈希值造成的,会阻碍性能。为了提供可接受的复制速度,对象复制被设计为每天使正常节点上的哈希空间失效约 2%。