跳转至

第三章:数据复制

3.1 复制模式

主从复制

主从复制架构:

┌─────────────────────────────────────────────────────────────┐
│                                                              │
│  ┌─────────────┐                                           │
│  │   Client    │                                           │
│  └──────┬──────┘                                           │
│         │                                                   │
│         ▼                                                   │
│  ┌─────────────┐                                           │
│  │   Primary   │ ← 写入                                    │
│  │   (Master)  │                                           │
│  └──────┬──────┘                                           │
│         │                                                   │
│         │ 复制                                              │
│         ├────────────────┬────────────────┐                │
│         ▼                ▼                ▼                │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │  Replica 1  │ │  Replica 2  │ │  Replica 3  │          │
│  │  (Slave)    │ │  (Slave)    │ │  (Slave)    │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
│         ↑                ↑                ↑                │
│         │                │                │                │
│         └────────────────┴────────────────┘                │
│                      读取                                   │
│                                                              │
│  特点:                                                     │
│  - 写入通过主节点                                          │
│  - 读取可以分散到从节点                                    │
│  - 主节点故障需要选举新主                                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

多主复制

多主复制架构:

┌─────────────────────────────────────────────────────────────┐
│                                                              │
│  数据中心 A                    数据中心 B                    │
│  ┌─────────────────┐          ┌─────────────────┐          │
│  │  ┌───────────┐  │          │  ┌───────────┐  │          │
│  │  │  Master A │◄─┼──────────┼─►│  Master B │  │          │
│  │  └─────┬─────┘  │          │  └─────┬─────┘  │          │
│  │        │        │          │        │        │          │
│  │   ┌────┴────┐   │          │   ┌────┴────┐   │          │
│  │   ▼         ▼   │          │   ▼         ▼   │          │
│  │ ┌─────┐ ┌─────┐ │          │ ┌─────┐ ┌─────┐ │          │
│  │ │Rep.1│ │Rep.2│ │          │ │Rep.1│ │Rep.2│ │          │
│  │ └─────┘ └─────┘ │          │ └─────┘ └─────┘ │          │
│  └─────────────────┘          └─────────────────┘          │
│                                                              │
│  特点:                                                     │
│  - 多个主节点可写入                                        │
│  - 需要处理写冲突                                          │
│  - 适合多地域部署                                          │
│                                                              │
└─────────────────────────────────────────────────────────────┘

3.2 复制策略

同步 vs 异步

# 同步复制
def sync_replication(data):
    """同步复制:等待所有副本确认"""
    primary.write(data)

    for replica in replicas:
        success = replica.write_sync(data)
        if not success:
            raise ReplicationError(f"Replication failed to {replica}")

    return Success()

# 异步复制
def async_replication(data):
    """异步复制:立即返回,后台复制"""
    primary.write(data)

    # 异步发送到副本
    for replica in replicas:
        threading.Thread(
            target=replica.write_async,
            args=(data,)
        ).start()

    return Success()

# 半同步复制
def semi_sync_replication(data):
    """半同步:等待至少一个副本确认"""
    primary.write(data)

    # 等待至少一个副本
    confirmed = 0
    for replica in replicas:
        if replica.write_sync(data):
            confirmed += 1
            if confirmed >= 1:
                break

    return Success()

3.3 冲突解决

最后写入胜利

import time

class LWWRegister:
    """Last-Write-Wins 寄存器"""

    def __init__(self):
        self.value = None
        self.timestamp = 0

    def write(self, value):
        timestamp = time.time()
        if timestamp > self.timestamp:
            self.value = value
            self.timestamp = timestamp

    def merge(self, other):
        """合并其他副本的数据"""
        if other.timestamp > self.timestamp:
            self.value = other.value
            self.timestamp = other.timestamp

向量时钟

class VectorClock:
    """向量时钟"""

    def __init__(self, node_id):
        self.node_id = node_id
        self.clock = {}

    def increment(self):
        """本地事件递增"""
        self.clock[self.node_id] = self.clock.get(self.node_id, 0) + 1

    def merge(self, other):
        """合并其他向量时钟"""
        for node, time in other.clock.items():
            self.clock[node] = max(self.clock.get(node, 0), time)

    def compare(self, other):
        """比较两个向量时钟"""
        all_nodes = set(self.clock.keys()) | set(other.clock.keys())

        self_greater = False
        other_greater = False

        for node in all_nodes:
            self_time = self.clock.get(node, 0)
            other_time = other.clock.get(node, 0)

            if self_time > other_time:
                self_greater = True
            elif other_time > self_time:
                other_greater = True

        if self_greater and not other_greater:
            return 1  # self 更新
        elif other_greater and not self_greater:
            return -1  # other 更新
        else:
            return 0  # 并发,需要解决冲突

3.4 小结

本章介绍了数据复制:

  1. 主从复制简单但主节点是瓶颈
  2. 多主复制支持多写但需处理冲突
  3. 同步复制保证一致性但延迟高
  4. 向量时钟解决因果顺序

思考题

  1. 如何选择复制策略?
  2. 如何处理网络分区?
  3. 如何实现无冲突数据类型?

参考资料