Redis 主从复制
1. 为什么需要主从复制
Section titled “1. 为什么需要主从复制”单节点 Redis 存在两个核心问题:
单节点的问题:┌──────────────┐│ Redis 单节点 │ ← 所有读写压力集中│ 读 + 写 │ ← 单点故障,宕机即不可用└──────────────┘主从复制的收益:
| 收益 | 说明 |
|---|---|
| 读写分离 | Master 处理写,Slave 分担读,横向扩展读性能 |
| 数据冗余 | 数据同步到多个节点,防止单点数据丢失 |
| 高可用基础 | 为哨兵模式和集群模式提供故障转移能力 |
2. 主从架构
Section titled “2. 主从架构” 写请求 │ ▼ ┌─────────────┐ │ Master │ ← 处理所有写操作 └──────┬──────┘ 异步复制│ ┌───────────┼───────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Slave 1 │ │ Slave 2 │ │ Slave 3 │ ← 处理读请求 └──────────┘ └──────────┘ └──────────┘ 读请求 读请求 读请求角色说明:
| 角色 | 职责 | 数量 |
|---|---|---|
| Master(主节点) | 处理写操作,将数据同步给 Slave | 1 个 |
| Slave(从节点) | 接收 Master 数据,处理读操作 | 1 个或多个 |
3. 数据同步原理
Section titled “3. 数据同步原理”3.1. 全量同步(首次连接)
Section titled “3.1. 全量同步(首次连接)”Slave 第一次连接 Master 时,执行全量数据同步:
全量同步流程:
Slave Master │─── replicaof <host> <port> ──▶│ │◀── 发送 replid + offset ──────│ │ │ │(第一次,replid 不匹配) │ │ ├── BGSAVE 生成 RDB 文件 │◀────────── 传输 RDB ───────────│ │ │ │── 加载 RDB ──▶│ │ │ │ │◀── 传输 RDB 期间的增量命令 ─────│ ← repl_backlog 缓冲区 │ │ │── 执行增量命令 ──▶│ │ │ │ │ 同步完成,进入持续同步阶段 │关键参数:
# repl_backlog:环形缓冲区,存储 Master 最近的写命令# 默认大小 1MB,用于增量同步时补发缺失命令repl-backlog-size 1mb3.2. 增量同步(断线重连)
Section titled “3.2. 增量同步(断线重连)”Slave 短暂断线重连后,不需要全量同步,只同步缺失的命令:
增量同步条件(同时满足):1. Slave 的 replid 与 Master 相同(同一主节点)2. Slave 的 offset 在 repl_backlog 范围内(未被覆盖)
增量同步流程:Slave ──▶ 发送 replid + offsetMaster ──▶ 从 repl_backlog 中找到 offset 之后的命令 ──▶ 只发送增量命令(非全量)3.3. 持续同步(正常运行)
Section titled “3.3. 持续同步(正常运行)”主从连接正常时,Master 每执行一条写命令,异步发送给所有 Slave:
Master 执行 SET key val │ ├── 写入本地内存 └── 异步发送命令给所有 Slave │ Slave 接收并执行(异步,存在毫秒级延迟)4. 环境搭建(Docker Compose)
Section titled “4. 环境搭建(Docker Compose)”# docker-compose.yml(1 Master + 2 Slave)version: '3'services: redis-master: image: redis:7.0 container_name: redis-master ports: - "6379:6379" command: redis-server --requirepass 123456 --appendonly yes
redis-slave1: image: redis:7.0 container_name: redis-slave1 ports: - "6380:6379" command: > redis-server --requirepass 123456 --masterauth 123456 --replicaof redis-master 6379 --appendonly yes
redis-slave2: image: redis:7.0 container_name: redis-slave2 ports: - "6381:6379" command: > redis-server --requirepass 123456 --masterauth 123456 --replicaof redis-master 6379 --appendonly yes验证主从同步:
# 在 Master 写入redis-cli -p 6379 -a 123456 SET test_key "hello"
# 在 Slave 读取(数据应已同步)redis-cli -p 6380 -a 123456 GET test_key # → "hello"redis-cli -p 6381 -a 123456 GET test_key # → "hello"
# 查看主从状态redis-cli -p 6379 -a 123456 INFO replication5. SpringBoot 读写分离配置
Section titled “5. SpringBoot 读写分离配置”spring: data: redis: # 主节点(写) host: 192.168.1.10 port: 6379 password: 123456 lettuce: cluster: refresh: adaptive: true手动配置读写分离(Lettuce):
@Configurationpublic class RedisReadWriteConfig {
@Bean public LettuceConnectionFactory redisConnectionFactory() { // Master 节点(写) RedisStandaloneConfiguration master = new RedisStandaloneConfiguration("192.168.1.10", 6379); master.setPassword("123456");
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() .readFrom(ReadFrom.REPLICA_PREFERRED) // 优先从 Slave 读,Slave 不可用时从 Master 读 .build();
return new LettuceConnectionFactory(master, clientConfig); }}ReadFrom 策略:
| 策略 | 说明 |
|---|---|
MASTER | 只从 Master 读 |
REPLICA | 只从 Slave 读,无 Slave 时报错 |
REPLICA_PREFERRED | 优先 Slave,无 Slave 时从 Master 读 ✅ |
NEAREST | 从延迟最低的节点读 |
6. 主从复制局限性
Section titled “6. 主从复制局限性”| 问题 | 说明 | 解决方案 |
|---|---|---|
| Master 单点故障 | Master 宕机后无法写入,需人工切换 | 哨兵模式(自动故障转移) |
| 异步复制数据丢失 | Master 宕机前未同步到 Slave 的数据丢失 | min-replicas-to-write 配置 |
| 扩展性有限 | 单 Master 写入能力有上限 | Redis Cluster(数据分片) |
最小同步副本数(减少数据丢失):
# redis.conf(Master 配置)# 要求至少 1 个 Slave 完成同步才响应写请求# 若 Slave 数量不足或同步延迟超过 10s,Master 拒绝写入min-replicas-to-write 1min-replicas-max-lag 10