分布式系统学习笔记(五):复制与 Gossip

写在前面

本文是分布式系列收官篇,讲数据复制(多副本如何同步)和 Gossip 协议(最终一致的传播机制),顺带讲分布式 ID 和限流。这些是 Cassandra、Redis Cluster、Consul、比特币网络背后的技术。


一、为什么要复制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
数据复制 = 在多个节点存副本

目的:
  ✓ 高可用(一个挂了,其他还能服务)
  ✓ 读扩展(多副本分担读压力)
  ✓ 就近访问(副本部署在不同地域,降低延迟)
  ✓ 容灾(异地备份)

代价:
  ✗ 一致性问题(副本间可能不一致)
  ✗ 写入开销(要同步到多个副本)
  ✗ 冲突(多副本同时改)

二、三种复制架构

2.1 主从复制(Single-Leader)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
一个主(Leader)负责写,多个从(Follower)复制

  客户端 → Leader(写)→ 复制 → Follower1, Follower2
  客户端 ← Follower(读)

特点:
  ✓ 写入无冲突(只有 Leader 写)
  ✓ 简单
  ✗ Leader 是写瓶颈和单点
  ✗ 主从延迟(异步复制时从节点数据滞后)

代表:MySQL 主从、Redis 主从、PostgreSQL 流复制、Kafka 分区

2.2 多主复制(Multi-Leader)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
多个 Leader 都能写,互相复制

  Leader1 ←→ Leader2
    ↓           ↓
  副本          副本

特点:
  ✓ 写可用性高(一个 Leader 挂了,另一个还能写)
  ✓ 适合多数据中心
  ✗ 写冲突(两个 Leader 同时改同一数据)
  ✗ 冲突解决复杂

代表:CouchDB、多数据中心部署的 MySQL(双主)

2.3 无主复制(Leaderless)

1
2
3
4
5
6
7
8
9
没有 Leader,客户端写多个节点,读多个节点比对

  客户端 → 写 N 个节点(W 个成功即算成功)
  客户端 ← 读 N 个节点(R 个返回一致即最新)

  W + R > N 保证强一致(Quorum)
  典型:W=R=多数

代表:Cassandra、DynamoDB、Riak(Dynamo 风格)

三、同步 vs 异步复制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
同步复制:
  Leader 写后,等所有 Follower 确认才返回客户端
  ✓ 强一致  ✗ 慢、一个 Follower 慢全拖慢

异步复制:
  Leader 写后立即返回,Follower 异步追赶
  ✓ 快  ✗ 数据可能滞后、Leader 挂了可能丢未同步数据

半同步复制:
  至少一个 Follower 同步确认,其他异步
  折中(MySQL 半同步复制)

  Redis 主从、MySQL 默认都是异步(性能优先)

四、复制日志格式

1
2
3
4
5
6
7
8
基于语句:复制 SQL 语句(now()、rand() 等有问题)
基于行(Row):复制数据行的变更(最常用,准确)
混合模式:语句 + 行
逻辑日志:复制逻辑变更(跨数据库兼容)

  MySQL binlog 支持 STATEMENT/ROW/MIXED
  Kafka 用消息本身做日志
  Redis 用命令/字节流

五、冲突解决

多主/无主复制下,并发写同一数据会产生冲突:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
1. 最后写入获胜(LWW)
   用时间戳,新的覆盖旧的
   ✗ 时钟漂移导致丢数据
   ✗ 简单但会丢更新(适合可容忍的场景)

2. 向量时钟(Vector Clock)
   记录每个写操作的逻辑时钟
   能检测并发冲突,交给应用解决
   ✓ 不依赖物理时钟  ✗ 复杂

3. 应用层解决
   冲突版本都返回,应用合并(如购物车合并)
   代表:Riak、CouchDB

4. CRDT(Conflict-free Replicated Data Type)
   无冲突复制数据类型,自动可合并
   ✓ 自动解决冲突  ✗ 数据类型受限(计数器、集合等)
   代表:Redis(部分)、Automerge、Yjs(协同编辑)

六、Gossip 协议(流言协议)

最终一致系统的数据传播机制,像病毒扩散/流言传播。

1
2
3
4
5
6
7
8
Gossip 核心思路:
  每个节点周期性随机挑几个节点,交换状态信息
  经过多轮,信息扩散到全网

  一轮 Gossip:
    Node A 随机选 B → 交换彼此知道的所有节点状态
    B 又选 C 交换 → ...
    O(log N) 轮后,全网收敛
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Gossip 的特点:
  ✓ 极高可扩展性(节点数再多也 O(log N) 收敛)
  ✓ 容错(节点挂了不影响传播)
  ✓ 去中心化(无主节点)
  ✓ 最终一致
  ✗ 不是强一致(有传播延迟)
  ✗ 可能传播过时信息(靠版本号解决)

代表:
  Cassandra — 节点状态、集群拓扑用 Gossip
  Consul    — 成员管理、故障检测用 Gossip
  Redis Cluster — 节点发现用 Gossip
  比特币     — 区块/交易传播
  HashiCorp Serf — Gossip 库

Gossip 与故障检测

1
2
3
4
5
6
Gossip 还用于故障检测(Phi Accrual Failure Detector):
  节点间互相发心跳(Gossip 带心跳)
  一段时间收不到某节点心跳 → 标记 suspect → 确认 down
  多个节点独立判断,避免单点误判

  比单纯超时更鲁棒(自适应、分布式)

七、分布式 ID

分布式系统需要全局唯一 ID:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
方案:
1. UUID
   ✓ 无中心、全局唯一  ✗ 长(128位)、无序、索引差

2. 雪花算法(Snowflake)
   64位 = 时间戳(41) + 机器ID(10) + 序列号(12)
   ✓ 趋势递增、高性能、无中心
   ✗ 依赖时钟(时钟回拨问题)

3. 数据库自增(分段)
   各节点分配不同 ID 段
   ✓ 简单  ✗ 依赖 DB

4. Redis INCR
   ✓ 简单、递增  ✗ 依赖 Redis、单点

5. 号段模式(Leaf、Tinyid)
   预分配 ID 段,本地消费,用完再取
   ✓ 高性能  ✗ 实现稍复杂

  通用推荐:雪花算法 / 号段模式

八、分布式限流

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
单机限流(内存)管不到多机,需要分布式限流:

1. Redis + Lua(集中式)
   所有请求到 Redis 计数,Lua 保证原子
   ✓ 准确  ✗ Redis 单点/性能瓶颈

2. 令牌桶(Redis 实现)
   按速率生成令牌,请求消耗令牌

3. 本地限流 + 中心同步
   各节点本地限流(配额的 1/N),定期同步
   ✓ 性能好  ✗ 不精确

  .NET 8 内置限流 + Redis 可做分布式

九、小结

  • 复制架构:主从(单Leader)、多主(多Leader)、无主(Leaderless + Quorum)
  • 复制方式:同步(强一致慢)、异步(快有延迟)、半同步(折中)
  • 冲突解决:LWW(简单丢数据)、向量时钟、应用层、CRDT(自动)
  • Gossip:流言式传播,O(log N) 收敛,最终一致,高可扩展
  • 分布式 ID:雪花算法(趋势递增无中心)、号段模式
  • 分布式限流:Redis + Lua 集中式、本地+中心同步

系列总结

分布式系统五篇完结:

  1. CAP 与一致性:CP vs AP,BASE,一致性模型
  2. Raft 共识:Leader 选举 + 日志复制 + Quorum,现代 CP 系统标配
  3. 分布式锁:Redis(AP 快)/ ZK·etcd(CP 可靠)
  4. 分布式事务:2PC/TCC/Saga/本地消息表,优先最终一致
  5. 复制与 Gossip:主从/多主/无主,Gossip 最终一致传播

核心心法:分布式系统的本质是"在不可靠的网络和节点上,构建可靠的协同"。CAP、共识、复制、一致性,都是在回答"如何取舍"的问题。没有完美方案,只有适合场景的方案。