# Redis 主从复制的实现原理是什么?

10 min read
Table of Contents

Redis 主从复制的实现原理是什么?

Redis 主从复制实现原理

【核心定义】

Redis 主从复制是通过命令传播机制,将主节点的数据变更操作实时同步到从节点,实现数据冗余和高可用的核心机制。

【关键要点】

  1. 全量同步(RDB快照):从节点首次连接或复制偏移量不匹配时,主节点生成RDB快照发送给从节点,完成基础数据同步
  2. 部分同步(命令传播):全量同步后,主节点将每个写命令通过复制积压缓冲区传播给从节点,实现增量更新
  3. 心跳检测:从节点每秒向主节点发送REPLCONF ACK <offset>,维持连接活性并报告复制进度
  4. 无盘复制:Redis 2.8.18+支持,主节点直接将RDB数据通过网络发送给从节点,避免磁盘IO瓶颈
  5. 级联复制:从节点可以作为其他从节点的主节点,形成树状复制拓扑

【深度推导/细节】

复制建立过程(分步拆解)

Step 1 - 连接建立

从节点执行:SLAVEOF <master_ip> <master_port>
1. 从节点保存主节点信息,清空自身数据
2. 建立与主节点的socket连接
3. 发送PING命令检测连接状态

Step 2 - 身份验证

1. 如果主节点配置了requirepass,从节点发送AUTH命令
2. 验证失败则复制终止,成功则继续下一步

Step 3 - 端口监听

从节点发送:REPLCONF listening-port <port>
告知主节点自己的监听端口,用于后续主节点向从节点推送数据

Step 4 - 同步协商

1. 从节点发送:PSYNC <runid> <offset>
- runid:主节点运行ID(40字符随机字符串)
- offset:从节点当前复制偏移量(首次为-1)
2. 主节点响应:
- +FULLRESYNC <runid> <offset>:需要全量同步
- +CONTINUE:可以部分同步

Step 5 - 数据同步

情况A:全量同步(从节点offset=-1或不在复制积压缓冲区范围内)
1. 主节点执行bgsave生成RDB快照
2. 将RDB文件发送给从节点
3. 从节点清空旧数据,加载RDB
4. 主节点将生成RDB期间的写命令存入复制缓冲区
5. RDB加载完成后,主节点发送缓冲区中的命令
情况B:部分同步(从节点offset在复制积压缓冲区中)
1. 主节点从复制积压缓冲区找到offset之后的所有命令
2. 直接发送这些命令给从节点

复制积压缓冲区设计

# 复制积压缓冲区是主节点维护的固定大小环形队列
class ReplicationBacklog:
def __init__(self, size=1MB): # 默认1MB,可配置
self.buffer = bytearray(size)
self.offset = 0 # 当前写入位置
self.size = size
def write(self, command):
# 写入命令并更新offset
# 如果缓冲区满,覆盖最旧的数据

缓冲区大小计算:

建议大小 = 网络延迟(秒) × 主节点每秒写命令数 × 平均命令大小 × 2
例如:延迟0.1s × 10000命令/秒 × 100字节 × 2 = 200KB
实际配置通常为32MB-256MB

【关联/对比】

Redis复制 vs MySQL复制

维度Redis主从复制MySQL主从复制
同步方式异步(默认)/ 半同步(Redis 6.0+)异步/半同步/全同步
数据格式RDB快照 + Redis协议命令binlog事件
复制粒度命令级复制行级/语句级复制
延迟处理复制积压缓冲区relay log
故障恢复PSYNC 2.0支持部分重同步基于GTID或binlog位置

Redis复制 vs Kafka副本

  • 数据模型:Redis是内存KV,Kafka是持久化日志
  • 一致性:Redis默认异步弱一致,Kafka支持ISR强一致
  • 选举机制:Redis哨兵/集群选举,Kafka Controller选举
  • 用途差异:Redis复制侧重高可用,Kafka复制侧重数据持久化

【版本差异】

Redis 2.8 之前

  • 只有全量复制,网络闪断需要重新全量同步
  • 复制中断后恢复效率极低

Redis 2.8(PSYNC 1.0)

  • 引入部分重同步机制
  • 新增复制积压缓冲区
  • 支持断线重连后增量同步

Redis 4.0

  • PSYNC 2.0:优化复制协议,解决重启后全量同步问题
  • 混合持久化:RDB+AOF,加速重启恢复

Redis 6.0

  • 无盘复制优化:支持socket直接传输RDB
  • 副本读取:从节点可处理只读请求
  • 多线程I/O:提升复制网络吞吐

Redis 7.0

  • 多AOF文件:更精细的持久化控制
  • 复制性能优化:减少主节点内存占用

『面试官追问』

Q1:主从复制延迟过大怎么办?

A:可以从四个层面优化:

  1. 网络层:主从同机房部署,使用万兆网络
  2. 配置层:
    Terminal window
    # 增大复制积压缓冲区
    repl-backlog-size 256mb
    # 调整客户端输出缓冲区限制
    client-output-buffer-limit slave 512mb 256mb 60
  3. 架构层:
    • 使用级联复制分散主节点压力
    • 业务侧读写分离,对延迟不敏感查询走从节点
  4. 监控层:监控master_repl_offset和slave_repl_offset差值

Q2:为什么Redis默认使用异步复制?

A:设计权衡的结果:

  • 性能优先:异步复制不阻塞主节点,吞吐量高(10万+ QPS)
  • 可用性:即使从节点故障,主节点仍可服务
  • 一致性妥协:可能丢失最后几毫秒数据,但Redis定位是缓存
  • 可配置性:Redis 6.0+支持WAIT命令实现同步复制

Q3:复制风暴问题如何解决?

A:复制风暴指大量从节点同时全量同步导致主节点负载过高:

  1. 树状复制:设置中间层从节点,分摊主节点压力
    主节点 → 从节点A → 从节点B
    ↓
    从节点C → 从节点D
  2. 错峰同步:分批重启从节点,避免同时全量同步
  3. 无盘复制:减少主节点磁盘IO压力
  4. 适当调大repl-backlog-ttl:延长缓冲区保留时间

Q4:PSYNC 2.0解决了什么问题?

A:主要解决两个痛点:

  1. 主节点重启后全量同步:

    • PSYNC 1.0:主节点重启后runid变化,触发全量同步
    • PSYNC 2.0:引入replid和replid2,重启后仍能识别旧复制关系
  2. 故障切换后的同步优化:

    # 故障切换场景
    旧主:M1 (replid: R1, offset: 1000)
    新主:S1 (原从节点,继承R1为replid2)
    其他从节点:S2 (offset: 800)
    # PSYNC 2.0下,S2可以向S1发送:
    PSYNC R1 800 # 可以部分同步
    # 而PSYNC 1.0需要全量同步

Q5:如何保证复制数据的一致性?

A:多级保障机制:

  1. 偏移量校验:通过master_repl_offset和slave_repl_offset比对
  2. 心跳检测:从节点每秒上报自身偏移量
  3. 数据校验:RDB文件传输时使用CRC64校验和
  4. 命令重放:从节点严格按顺序执行主节点命令
  5. 手动校验:使用INFO replication和redis-cli --slave监控

Q6:复制超时相关参数有哪些?

A:关键参数及默认值:

Terminal window
repl-timeout 60 # 复制超时时间(秒)
repl-ping-slave-period 10 # 主节点ping从节点间隔
repl-disable-tcp-nodelay no # 是否启用Nagle算法

设计考量:

  • repl-timeout=60:平衡网络延迟和故障检测灵敏度
  • repl-ping-slave-period=10:足够频繁以快速发现故障,又不至于产生过多流量

总结要点:Redis主从复制的核心是PSYNC协议,通过复制积压缓冲区实现断线重连后的增量同步,设计上在性能和数据一致性间取得平衡,不同版本持续优化复制效率和可靠性。

My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# Redis 中跳表的实现原理是什么?

9 min read

跳表是一种基于有序链表、通过添加多级索引实现快速查找、插入和删除的概率平衡数据结构,在Redis中主要用于实现有序集合(Sorted Set)的底层存储。

Read

Comments