Redis 源码硬核解析系列专题 - 第七篇:主从复制与集群模式

第七篇:主从复制与集群模式

1. 引言

Redis通过主从复制实现高可用性,通过集群模式实现数据分片和分布式扩展。本篇将深入剖析主从复制(replication.c)和Redis Cluster(cluster.c)的源码实现,揭示其同步机制、故障转移和分片逻辑。


2. 主从复制

2.1 主从同步概述
  • 作用:从节点复制主节点数据,提供读扩展和容错。
  • 方式
    • 全量同步(Full Resync):初次同步或数据差异过大。
    • 增量同步(Partial Resync):通过复制偏移量同步增量命令。
2.2 全量同步(syncCommand()

代码片段replication.c):

1
2
3
4
5
6
7
8
9
void syncCommand(client *c) {
    if (server.masterhost == NULL) { // 主节点处理
        if (c->flags & CLIENT_SLAVE) return;
        c->flags |= CLIENT_SLAVE;
        listAddNodeTail(server.slaves, c);
        replicationSendNewline(c);
        replicationSendRdb(c); // 发送RDB文件
    }
}

硬核解析

  • CLIENT_SLAVE:标记从节点客户端。
  • replicationSendRdb():主节点生成RDB快照并发送。
2.3 增量同步(PSYNC)

代码片段replication.c):

1
2
3
4
5
6
7
8
void replicationPSync(client *c, char *replid, long long offset) {
    if (memcmp(replid, server.replid, CONFIG_RUN_ID_SIZE) == 0 &&
        offset >= server.repl_backlog_first_byte_offset) {
        replicationSendContinuation(c, offset); // 增量同步
    } else {
        replicationSendFullResync(c); // 全量同步
    }
}

硬核解析

  • replid:复制ID,标识主节点实例。
  • repl_backlog:环形缓冲区,保存近期写命令。
  • 条件:ID匹配且偏移量在backlog内则增量同步,否则全量。

Mermaid同步流程

graph TD
    A["PSYNC命令"] --> B{"replid匹配且offset有效?"}
    B -->|Yes| C["replicationSendContinuation()"]
    C --> D["发送backlog中命令"]
    B -->|No| E["replicationSendFullResync()"]
    E --> F["发送RDB文件"]

3. 集群模式

3.1 集群架构
  • 分片:16384个槽(slot),每个节点负责一部分。
  • 通信:节点间通过Gossip协议交换状态。

代码片段cluster.h):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
typedef struct clusterNode {
    char name[CLUSTER_NAMELEN]; // 节点ID
    uint16_t port;              // 端口
    int flags;                  // 状态标志
    unsigned char slots[16384/8]; // 槽位图
    struct clusterLink *link;   // 连接对象
} clusterNode;

typedef struct clusterState {
    clusterNode *myself;        // 本节点
    dict *nodes;                // 所有节点
    int slots[16384];           // 槽到节点的映射
} clusterState;

硬核解析

  • slots:位图表示节点负责的槽。
  • nodes:字典存储集群所有节点。
3.2 槽分配与查询

代码片段cluster.c):

1
2
3
4
5
6
7
8
int clusterGetSlotMaster(int slot) {
    return server.cluster->slots[slot];
}

void clusterAddSlot(clusterNode *n, int slot) {
    n->slots[slot / 8] |= (1 << (slot % 8));
    server.cluster->slots[slot] = n->numslots++;
}

硬核解析

  • 哈希计算CRC16(key) % 16384决定槽。
  • 位图操作:高效标记槽归属。
3.3 故障转移(Failover)

代码片段cluster.c):

1
2
3
4
5
6
7
void clusterFailoverReplaceMaster(clusterNode *n) {
    clusterNode *master = n->slaveof;
    if (master->flags & CLUSTER_NODE_FAIL) {
        clusterSetMaster(n); // 从变主
        replicationSendUpdate(n); // 通知其他节点
    }
}

硬核解析

  • 触发:主节点标记为FAIL(超时未响应)。
  • 选举:从节点竞争成为新主,需多数节点同意。

Mermaid故障转移流程

graph TD
    A["主节点故障"] --> B{"从节点检测到FAIL?"}
    B -->|Yes| C["clusterFailoverReplaceMaster()"]
    C --> D["从变主"]
    D --> E["更新槽映射"]
    E --> F["通知集群"]

4. 优化与设计

  • 主从复制
    • backlog减少全量同步开销。
    • 异步复制提升性能。
  • 集群
    • 16384槽平衡分片与开销。
    • Gossip协议确保一致性。

5. 总结与调试建议

  • 收获:理解主从复制与集群的实现。
  • 调试技巧
    • INFO REPLICATION查看同步状态。
    • CLUSTER NODES检查槽分配。
  • 下一步:回顾Redis设计哲学。
updatedupdated2025-03-312025-03-31