10 - 主从复制
主从复制
10.1 复制概述
Redis 复制(Replication)允许一个 Redis 服务器(从节点/Slave)复制另一个 Redis 服务器(主节点/Master)的数据。主节点负责写入,从节点负责读取,实现读写分离。
复制架构
┌──────────┐
│ Master │ ← 写入
│ (主节点) │
└────┬─────┘
│
┌─────────┼─────────┐
│ │ │
┌─────▼───┐ ┌───▼────┐ ┌─▼───────┐
│ Slave 1 │ │ Slave 2│ │ Slave 3 │ ← 读取
│ (从节点)│ │(从节点) │ │ (从节点) │
└─────────┘ └────────┘ └─────────┘
复制特性
| 特性 | 说明 |
|---|---|
| 异步复制 | 主节点不会等待从节点确认 |
| 单向复制 | 数据只能从主到从 |
| 多个从节点 | 一个主节点可以有多个从节点 |
| 链式复制 | 从节点可以有自己的从节点 |
| 非阻塞 | 复制期间主从节点都可以正常处理请求 |
10.2 配置主从复制
方式一:配置文件
# 从节点 redis.conf
replicaof 192.168.1.100 6379
# 如果主节点有密码
masterauth YourStr0ngP@ssword
# 从节点只读(推荐)
replica-read-only yes
# 从节点是否使用磁盘持久化
replica-lazy-flush no
方式二:运行时命令
# 在从节点上执行
redis-cli
> REPLICAOF 192.168.1.100 6379
OK
# 设置主节点密码
> CONFIG SET masterauth YourStr0ngP@ssword
# 取消复制(变为独立主节点)
> REPLICAOF NO ONE
方式三:Docker Compose
version: '3.8'
services:
redis-master:
image: redis:7.2
container_name: redis-master
ports:
- "6379:6379"
volumes:
- ./master-data:/data
command: redis-server --requirepass mypassword --appendonly yes
networks:
- redis-net
redis-slave-1:
image: redis:7.2
container_name: redis-slave-1
ports:
- "6380:6379"
command: redis-server --replicaof redis-master 6379 --masterauth mypassword --appendonly yes
depends_on:
- redis-master
networks:
- redis-net
redis-slave-2:
image: redis:7.2
container_name: redis-slave-2
ports:
- "6381:6379"
command: redis-server --replicaof redis-master 6379 --masterauth mypassword --appendonly yes
depends_on:
- redis-master
networks:
- redis-net
networks:
redis-net:
driver: bridge
10.3 复制原理
全量同步(Full Resynchronization)
首次连接或无法增量同步时触发:
从节点 主节点
│ │
│ REPLICAOF host port │
│────────────────────────→│
│ │
│ PSYNC ? -1 │ ← 首次同步,offset 为 -1
│────────────────────────→│
│ │
│ │ BGSAVE 生成 RDB
│ │ 记录复制缓冲区
│ │
│ FULLRESYNC <runid> <offset>
│←────────────────────────│
│ │
│ (等待 RDB 文件) │
│←────────────────────────│ 发送 RDB 文件
│ │
│ 加载 RDB 到内存 │
│ │
│ (等待增量数据) │
│←────────────────────────│ 发送复制缓冲区中的增量命令
│ │
│ (同步完成,开始正常复制) │
增量同步(Partial Resynchronization)
断线重连后的增量同步(Redis 2.8+):
从节点 主节点
│ │
│ (断线重连) │
│ PSYNC <runid> <offset> │
│────────────────────────→│
│ │ 检查 offset 是否在复制积压缓冲区中
│ │
│ CONTINUE │ ← 增量同步
│←────────────────────────│
│ │
│ (从断点处继续接收命令) │
│←────────────────────────│ 发送 offset 之后的命令
复制积压缓冲区(Replication Backlog)
# 复制积压缓冲区大小
repl-backlog-size 256mb
# 无从节点时释放缓冲区的时间(秒,0 表示不释放)
repl-backlog-ttl 3600
缓冲区大小计算公式:
repl-backlog-size = 断线时间(秒) × 写入 QPS × 平均命令大小
# 示例:假设断线 60 秒,写入 QPS 10000,平均命令 100 字节
repl-backlog-size = 60 × 10000 × 100 = 60000000 ≈ 57 MB
# 建议设为计算值的 2 倍:114 MB
10.4 复制状态监控
# 主节点查看复制状态
redis-cli INFO replication
# role:master
# connected_slaves:2
# slave0:ip=192.168.1.101,port=6379,state=online,offset=12345,lag=0
# slave1:ip=192.168.1.102,port=6379,state=online,offset=12345,lag=1
# master_replid:abc123...
# master_repl_offset:12345
# repl_backlog_active:1
# repl_backlog_size:268435456
# repl_backlog_first_byte_offset:1
# repl_backlog_histlen:12345
# 从节点查看复制状态
redis-cli INFO replication
# role:slave
# master_host:192.168.1.100
# master_port:6379
# master_link_status:up
# master_last_io_seconds_ago:1
# master_sync_in_progress:0
# slave_read_repl_offset:12345
# slave_repl_offset:12345
# slave_priority:100
复制状态字段说明
| 字段 | 说明 |
|---|---|
master_link_status | 主从连接状态(up/down) |
master_last_io_seconds_ago | 上次主节点通信的秒数 |
master_sync_in_progress | 是否正在全量同步 |
slave_repl_offset | 从节点当前的复制偏移量 |
slave_priority | 从节点优先级(越小越优先被选为新主) |
slave_read_only | 从节点是否只读 |
10.5 哨兵 Sentinel
Sentinel 架构
┌────────────┐
│ Sentinel 1 │
└─────┬──────┘
│ 监控
┌───────────┼───────────┐
│ │ │
┌─────▼────┐ ┌───▼──────┐ ┌──▼────────┐
│ Sentinel 2│ │ Sentinel 3│ │ Master │
└─────┬────┘ └───┬──────┘ └──┬─────────┘
│ │ │
│ 监控 │ ┌────┼────────┐
│ │ │ │ │
│ ┌─────┼─────┼────┘ │
│ │ │ │ │
│ ┌──▼─────▼──┐ ┌▼─────────┐ │
│ │ Slave 1 │ │ Slave 2 │ │
│ └────────────┘ └──────────┘ │
│ │
└───── 故障检测 + 自动故障转移 ──┘
Sentinel 功能
| 功能 | 说明 |
|---|---|
| 监控 | 检测主从节点是否正常运行 |
| 通知 | 通过 Pub/Sub 通知客户端主节点变更 |
| 自动故障转移 | 主节点不可用时自动将从节点提升为主节点 |
| 配置中心 | 客户端连接 Sentinel 获取当前主节点地址 |
Sentinel 配置
# sentinel.conf
# 监控的主节点(quorum=2 表示至少 2 个 Sentinel 认为 Master 下线才触发故障转移)
sentinel monitor mymaster 192.168.1.100 6379 2
# 主节点密码
sentinel auth-pass mymaster YourStr0ngP@ssword
# 主节点无响应超时时间(毫秒)
sentinel down-after-milliseconds mymaster 5000
# 故障转移超时时间(毫秒)
sentinel failover-timeout mymaster 60000
# 并行同步的从节点数量
sentinel parallel-syncs mymaster 1
# 通知脚本(可选)
# sentinel notification-script mymaster /opt/scripts/notify.sh
# 客户端重配置脚本(可选)
# sentinel client-reconfig-script mymaster /opt/scripts/reconfig.sh
# 日志
loglevel notice
logfile /var/log/redis/sentinel.log
# Sentinel 端口
port 26379
Docker Compose 哨兵集群
version: '3.8'
services:
redis-master:
image: redis:7.2
container_name: redis-master
command: redis-server --requirepass mypassword --appendonly yes
networks:
- redis-net
redis-slave-1:
image: redis:7.2
container_name: redis-slave-1
command: redis-server --replicaof redis-master 6379 --masterauth mypassword --appendonly yes
depends_on:
- redis-master
networks:
- redis-net
redis-slave-2:
image: redis:7.2
container_name: redis-slave-2
command: redis-server --replicaof redis-master 6379 --masterauth mypassword --appendonly yes
depends_on:
- redis-master
networks:
- redis-net
sentinel-1:
image: redis:7.2
container_name: sentinel-1
command: >
redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel-1.conf:/etc/redis/sentinel.conf
depends_on:
- redis-master
- redis-slave-1
- redis-slave-2
networks:
- redis-net
sentinel-2:
image: redis:7.2
container_name: sentinel-2
command: >
redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel-2.conf:/etc/redis/sentinel.conf
depends_on:
- redis-master
- redis-slave-1
- redis-slave-2
networks:
- redis-net
sentinel-3:
image: redis:7.2
container_name: sentinel-3
command: >
redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel-3.conf:/etc/redis/sentinel.conf
depends_on:
- redis-master
- redis-slave-1
- redis-slave-2
networks:
- redis-net
networks:
redis-net:
driver: bridge
哨兵操作命令
# 连接 Sentinel
redis-cli -p 26379
# 查看监控的主节点
SENTINEL masters
# 查看主节点的从节点
SENTINEL replicas mymaster
# 获取当前主节点地址
SENTINEL get-master-addr-by-name mymaster
# 1) "192.168.1.100"
# 2) "6379"
# 查看 Sentinel 节点信息
SENTINEL sentinels mymaster
# 手动触发故障转移
SENTINEL failover mymaster
# 检查配置
SENTINEL ckquorum mymaster
Python 使用 Sentinel
from redis.sentinel import Sentinel
# 连接 Sentinel
sentinel = Sentinel([
('192.168.1.1', 26379),
('192.168.1.2', 26379),
('192.168.1.3', 26379),
], socket_timeout=0.5)
# 获取主节点连接
master = sentinel.master_for('mymaster', password='mypassword')
master.set('key', 'value')
# 获取从节点连接(读操作)
slave = sentinel.slave_for('mymaster', password='mypassword')
value = slave.get('key')
10.6 复制相关配置
# ---- 主节点配置 ----
# 写入复制缓冲区的最小 repl-offset 间隔
repl-diskless-sync yes # 无磁盘复制(直接通过 socket 发送 RDB)
repl-diskless-sync-delay 5 # 无磁盘复制延迟(秒)
repl-ping-replica-period 10 # 主节点 ping 从节点的间隔
# 从节点写入限制
# 如果从节点落后主节点超过 N 秒的 offset,则拒绝写入
min-replicas-to-write 1 # 至少 N 个从节点在线才接受写入
min-replicas-max-lag 10 # 从节点最大延迟(秒)
# ---- 从节点配置 ----
replica-serve-stale-data yes # 同步期间是否提供旧数据
replica-read-only yes # 从节点只读
replica-priority 100 # 故障转移优先级(0 表示不参与)
📌 业务场景
场景一:读写分离
# 写操作走主节点
redis-master SET user:1001 '{"name":"张三"}'
# 读操作走从节点
redis-slave GET user:1001
场景二:数据备份
# 从节点关闭持久化(由主节点负责持久化)
# 从节点用于数据备份和灾难恢复
场景三:高可用方案
# 3 个 Sentinel + 1 主 + 2 从
# 自动故障转移,客户端通过 Sentinel 发现主节点