Redis 的分布式设计主要通过三种模式实现,每种模式解决不同的问题:
一、三种分布式模式对比
| 模式 | 解决的问题 | 数据关系 | 适用场景 |
|---|---|---|---|
| 主从复制 | 读写分离、数据备份 | 全量复制 | 读多写少、数据备份 |
| 哨兵模式 | 高可用、自动故障转移 | 全量复制 | 需要自动容灾 |
| Cluster 集群 | 水平扩展、海量数据 | 分片存储 | 数据量大、需要扩展 |
二、主从复制(Replication)
核心机制
scss
┌─────────┐ 全量/增量复制 ┌─────────┐
│ Master │ ───────────────────────────► │ Slave │
│ (写) │ │ (读) │
└─────────┘ └─────────┘
复制流程
text
1. Slave 发送 SYNC/PSYNC 命令
2. Master 执行 BGSAVE,生成 RDB 文件
3. Master 将 RDB 发送给 Slave
4. Slave 加载 RDB
5. 后续写命令实时同步(增量复制)
关键配置
bash
# slave 配置
replicaof 192.168.1.10 6379
masterauth <password>
# 只读(默认)
replica-read-only yes
问题
- 无法自动故障转移:Master 挂了需要手动切换
- 写压力集中在 Master
- 复制延迟:Slave 数据可能落后
三、哨兵模式(Sentinel)
在主从基础上增加自动监控和故障转移。
架构
arduino
┌─────────┐
│ Client │
└────┬────┘
│
┌────────┼────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│Sentinel│ │Sentinel│ │Sentinel│ (至少3个,投票决策)
│ 1 │ │ 2 │ │ 3 │
└────┬───┘ └────┬───┘ └────┬───┘
│ │ │
└──────────┼──────────┘
▼
┌─────────────┐
│ Master │
└──────┬──────┘
│
┌──────┴──────┐
▼ ▼
┌───────┐ ┌───────┐
│ Slave1│ │ Slave2│
└───────┘ └───────┘
哨兵的核心功能
| 功能 | 说明 |
|---|---|
| 监控 | 定期检查 Master/Slave 是否存活 |
| 通知 | 故障时通知管理员或其他应用 |
| 自动故障转移 | Master 宕机时,选举 Slave 为新 Master |
| 配置提供者 | 客户端向 Sentinel 询问当前 Master 地址 |
故障转移流程
text
1. 主观下线(SDOWN):某个 Sentinel 发现 Master 无响应
2. 客观下线(ODOWN):多个 Sentinel 投票确认 Master 故障
3. 选举 Leader Sentinel:Raft 算法投票
4. 选择最优 Slave(优先级高、复制偏移量大、RunID小)
5. 提升为 Master,其他 Slave 重新配置
6. 通知客户端更新 Master 地址
客户端连接方式
python
# Python 示例:Sentinel 自动发现 Master
from redis.sentinel import Sentinel
sentinel = Sentinel([
('sentinel1', 26379),
('sentinel2', 26379),
('sentinel3', 26379)
])
# 自动获取当前 Master
master = sentinel.master_for('mymaster', password='xxx')
slave = sentinel.slave_for('mymaster', password='xxx')
master.set('key', 'value')
value = slave.get('key')
缺点
- 只有一个 Master 写,无法水平扩展写性能
- 数据量受限于单节点内存
四、Cluster 集群(最核心)
Redis 真正的分布式方案,通过数据分片实现水平扩展。
架构
css
┌─────────────────────────────────────────────┐
│ Redis Cluster │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ [0-5460]│ │[5461-10922]│[10923-16383] │
│ │ Master A│ │ Master B│ │ Master C│ │
│ └───┬─────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ┌───┴───┐ ┌───┴───┐ ┌───┴───┐ │
│ │Slave A1│ │Slave B1│ │Slave C1│ │
│ └────────┘ └────────┘ └────────┘ │
│ │
│ 16384 个哈希槽(Slot)均匀分配到各 Master │
└─────────────────────────────────────────────┘
核心概念:哈希槽(Hash Slot)
text
Slot = CRC16(key) % 16384
key "user:1000" → CRC16 = 12345 → 12345 % 16384 = 12345 → 属于 Node 3
为什么用 16384 个槽?
- CRC16 输出范围 0~65535
- 16384 = 2^14,平衡了元数据大小和分片粒度
- 集群节点配置中传播的是槽位映射,不是具体 key
节点通信:Gossip 协议
text
每个节点每秒随机向几个其他节点发送 PING
携带:自身状态 + 已知其他节点的状态
通过"谣言传播"方式,最终整个集群状态一致
客户端路由
text
┌─────────┐
│ Client │
└────┬────┘
│ SET key value
▼
┌─────────┐ 计算 key 的 Slot
│ Node 1 │ 发现 Slot 不在本地
└────┬────┘
│ 返回 MOVED 错误,告知正确节点
▼
┌─────────┐
│ Client │ 重定向到正确节点
└────┬────┘
▼
┌─────────┐
│ Node 3 │ 执行命令
└─────────┘
Smart Client(如 redis-py-cluster)会缓存槽位映射,减少重定向。
数据迁移(扩容/缩容)
text
1. 新节点加入集群
2. 分配部分 Slot 给新节点(如从 Node1 迁 1000 个 Slot)
3. 迁移过程中:
- 源节点:迁出的 Slot 标记为 IMPORTING
- 目标节点:标记为 MIGRATING
- 迁移中的 key:先查源节点,没有则查目标节点
4. 迁移完成后更新槽位映射
Cluster 配置示例
bash
# redis.conf
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 5000
cluster-require-full-coverage no # 部分槽不可用时不拒绝所有请求
bash
# 创建集群(6 节点,3 主 3 从)
redis-cli --cluster create \
192.168.1.11:6379 192.168.1.12:6379 192.168.1.13:6379 \
192.168.1.14:6379 192.168.1.15:6379 192.168.1.16:6379 \
--cluster-replicas 1
五、三种模式对比总结
| 维度 | 主从复制 | 哨兵模式 | Cluster 集群 |
|---|---|---|---|
| 数据容量 | 单机内存 | 单机内存 | 多机总和 |
| 写性能 | 单机 | 单机 | 水平扩展 |
| 读性能 | 扩展 | 扩展 | 扩展 |
| 高可用 | 手动切换 | 自动切换 | 自动切换 |
| 数据分片 | 否 | 否 | 是 |
| 复杂度 | 低 | 中 | 高 |
六、生产环境建议
| 场景 | 推荐方案 |
|---|---|
| 数据 < 10GB,读多写少 | 主从 + 哨兵 |
| 数据 > 10GB 或需要扩展写 | Cluster 集群 |
| 需要跨机房容灾 | Cluster + 主从交叉部署 |
| 超大规模(TB 级) | 业务分片 + 多 Cluster |
七、常见问题
1. Cluster 的 key 限制
bash
# 默认不支持跨 Slot 的多 key 操作
MGET key1 key2 # 如果 key1 和 key2 在不同 Slot,报错
# 解决方案:Hash Tag
MGET {user:1000}:name {user:1000}:age
# {} 内内容用于计算 Slot,确保同一用户数据在同一节点
2. 脑裂问题
text
网络分区时,原 Master 和 Sentinel 隔离
Sentinel 选举新 Master → 出现两个 Master
解决方案:min-slaves-to-write 配置,确保有足够 Slave 才接受写
3. 数据倾斜
text
Big Key 或 Hash Tag 使用不当导致某些 Slot 数据量过大
解决方案:监控 Slot 大小,避免 Big Key,合理设计 key