分布式:数据复制

可以把"数据复制"理解为:

同一份数据保存在多台服务器上,一台坏了,还能从其他服务器读取或恢复。

但仅仅复制多份还不够,系统还要解决:写入顺序、何时算成功、节点故障后由谁接管,以及不同副本如何重新同步。

一、最简单的多副本结构

假设有三个节点:

复制代码
节点 A:x = 0
节点 B:x = 0
节点 C:x = 0

客户端希望执行:

复制代码
SET x = 100

在 Raft 这类系统中,节点分为:

复制代码
A:Leader
B:Follower
C:Follower

所有写请求先交给 Leader:

复制代码
客户端
   |
   | SET x = 100
   v
Leader A
   |----------------> Follower B
   |
   +----------------> Follower C

Leader 负责规定操作顺序,Follower 按照这个顺序复制和执行。这样客户端虽然面对多台服务器,但逻辑上像在操作一台可靠的服务器。

二、一次写入是如何复制的

第一步:写入 Leader 日志

客户端发送:

复制代码
SET x = 100

Leader 先把它记录到日志中:

复制代码
A 的日志:

Index  Term  Command
1      1     SET x = 10
2      2     SET x = 100

此时日志 2 只是"Leader 收到了",还不能立即认为操作成功。

第二步:发送给其他节点

Leader 通过 AppendEntries 把日志复制给 B、C:

复制代码
A:[1, 2]
   |
   +---- 日志 2 ----> B:[1, 2]
   |
   +---- 日志 2 ----> C:[1, 2]

Follower 会先把日志持久化,再返回确认。

第三步:等待多数节点确认

假设 C 暂时断网:

复制代码
A:保存成功
B:保存成功
C:没有响应

三节点集群的多数是两个,因此 A 和 B 已经构成多数:

复制代码
3 个节点,多数 = 2
5 个节点,多数 = 3
7 个节点,多数 = 4

Leader 此时可以把日志标记为 Committed,然后应用到状态机,并通知客户端写入成功。共识系统只要多数节点仍可通信,通常就能继续推进;五节点集群可以容忍两个节点故障。

复制代码
A:x = 100,已提交
B:x = 100,已提交
C:暂时还是旧数据

三、为什么多数确认很重要

假设一条数据只保存在 Leader 上就返回成功:

复制代码
A:有日志 2,并向客户端返回成功
B:没有日志 2
C:没有日志 2

如果 A 马上损坏,日志 2 就彻底丢失了:

复制代码
A:故障
B:[1]
C:[1]

这意味着客户端明明收到"成功",数据却消失了。

如果要求多数节点保存:

复制代码
A:[1, 2]
B:[1, 2]
C:[1]

即使 A 故障,B 仍然拥有日志 2,可以参与选举并成为新 Leader。

所以多数确认的意义是:

一条已经宣布成功的数据,不能只存在于一台可能损坏的服务器上。

四、复制如何提高可用性

没有副本时:

复制代码
客户端 -> 节点 A

A 故障 -> 整个服务不可用

有三个副本时:

复制代码
客户端 -> Leader A
             |
             +--> B
             +--> C

如果 Follower C 故障:

复制代码
A:正常
B:正常
C:故障

A 和 B 仍然构成多数,系统可以继续处理请求。

如果 Leader A 故障:

复制代码
A:故障
B:正常
C:正常

B、C 会重新选举。其中拥有最新合格日志的节点成为新 Leader:

复制代码
B:新 Leader
C:Follower

客户端之后把请求发送给 B,服务得以恢复。Raft 的选举限制要求候选者的日志至少与投票节点一样新,防止缺少已提交记录的节点当选。

五、复制如何提高容错性

容错性表示系统的一部分发生故障时,整体仍然能够正确运行。

从节点故障

复制代码
A:Leader,正常
B:Follower,正常
C:Follower,故障

系统还有多数节点,继续工作。

C 恢复后,会向 Leader 补齐缺少的日志:

复制代码
恢复前:

A:[1, 2, 3, 4]
B:[1, 2, 3, 4]
C:[1, 2]

同步后:

C:[1, 2, 3, 4]

Leader 故障

剩余节点重新选举,新 Leader 接管请求。因为选举多数与提交多数必然存在重叠节点,再加上日志新旧检查,已经提交的日志会被后续 Leader 保留。

数据盘故障

只要其他副本仍然保存数据,故障节点修复后就可以从正常节点重新同步。

六、网络分区时会发生什么

假设五个节点被分成两组:

复制代码
多数一侧:A、B、C

      网络中断

少数一侧:D、E

多数一侧有三个节点,可以选举 Leader、复制日志并继续提交。

少数一侧只有两个节点:

复制代码
D + E < 多数 3

因此它们不能提交写入。即使旧 Leader 位于少数一侧,也不能在没有多数确认的情况下向客户端返回成功。

这会牺牲少数一侧的可用性,但能防止两边同时确认冲突数据:

复制代码
多数一侧:x = 100
少数一侧:x = 200

网络恢复后,少数一侧会接受新 Leader,并删除或覆盖未提交的冲突日志。因此 Raft 的取舍总体属于 CAP 中的 CP:发生网络分区时,优先保证一致性。

七、为什么不等待所有节点

假设系统规定必须三台全部写入成功:

复制代码
A 成功 + B 成功 + C 成功 -> 返回成功

只要 C 故障,所有写请求都会失败。数据一致性很好,但可用性很差。

如果只等待一台:

复制代码
A 成功 -> 立即返回

速度快、暂时更可用,但 A 故障时可能丢失刚写的数据。

多数派是一种折中:

复制代码
三台中写入两台 -> 成功
五台中写入三台 -> 成功

它允许少量节点故障,同时又能保护已提交的数据。

八、强一致和最终一致是两条不同路线

Raft:强一致路线

复制代码
写入 Leader
-> 复制到多数
-> 标记提交
-> 返回成功

如果无法联系多数节点,就停止提交,避免返回错误结果。

Dynamo 类系统:高可用路线

另一类系统允许多个可用节点继续接收写入:

复制代码
节点 A 接收:x = 100
节点 B 接收:x = 200

网络恢复后再通过版本号、时间戳或业务规则解决冲突。这种复制方式能够获得更高的分区可用性,但可能只能提供最终一致性。Amazon 的 Dynamo 设计通过多副本、类 quorum 技术和版本冲突处理,选择在部分故障场景中牺牲强一致性来提高可用性。

九、最重要的区分

复制代码
复制成功:
数据已经保存到某个副本

提交成功:
数据已经满足系统的确认规则,可以对外宣布成功

执行成功:
已提交日志已经应用到数据库或状态机

在 Raft 中,完整过程可以记成:

复制代码
客户端写入
    ↓
Leader 记录日志
    ↓
复制到 Followers
    ↓
多数节点确认
    ↓
日志提交
    ↓
各节点按顺序执行
    ↓
Leader 返回成功

因此,真正保证可用性和容错性的不是"复制"这一个动作,而是:

多副本保存 + 多数确认 + Leader 选举 + 日志补齐 + 冲突日志修复。

另外,多副本不等于备份。误删除或错误命令也可能被迅速复制到所有节点,所以实际系统通常还需要独立备份。

相关推荐
七夜zippoe8 小时前
深入解析CANN仓库中的HCCL分布式通信库
pytorch·分布式·cann·hccl·通信库
富士康质检员张全蛋8 小时前
Kafka的操作 消费者组 消费位置查看
分布式·kafka
运维行者_8 小时前
如何查看每个IP的带宽使用情况?NetFlow 技术实战指南
开发语言·网络·分布式·后端·架构·带宽
笨鸟先飞的橘猫8 小时前
游戏后端分布式学习——一致性协议Raft
分布式·学习·游戏
谢白羽8 小时前
vllm源码剖析14-vLLM 分布式推理-专家并行EP
笔记·分布式·llm·论文·vllm
霸道流氓气质11 小时前
Kiro 中配置 RabbitMQ MCP Server 指南
分布式·rabbitmq·ruby
国科安芯11 小时前
AS32S601型抗辐射MCU在分布式太空算力架构中的技术演进与应用前景
人工智能·分布式·单片机·嵌入式硬件·架构·边缘计算
不会C语言的菜鸟12 小时前
分布式数据一致性:从工程实践到理论基石
分布式
Leo.yuan1 天前
数据仓库建设怎么做?从源数据到分析报表全流程讲清
大数据·分布式·spark