Redis 集群模式
· 6 min read
核心问题
单机总有上限,集群才是归宿。 主从 + 哨兵解决了高可用,却无法突破单机存储与写性能的物理瓶颈。
| 方案 | 解决的问题 | 遗留问题 |
|---|---|---|
| 单机 Redis | 高性能 KV 存储 | 单点故障、CPU/内存上限 |
| 主从复制 + 哨兵 | 高可用(故障自动切换) | 存储全量数据、写仍集中主节点 |
| 集群模式 | 高可用 + 高可扩展 | — |
数据切分(分片)
核心思想
数据无限,单机有限 → 数据切分 → 分散到多个节点 → 水平扩容。
┌─────────────────────────────────────────┐
│ 全量数据 │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │片 1 │ │片 2 │ │片 3 │ │片 4 │ … │
│ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ │
│ ▼ ▼ ▼ ▼ │
│ 节点 A 节点 B 节点 C 节点 D │
└─────────────────────────────────────────┘
切分方案演进
方案一:简单求余法(不推荐)
slot = hash(key) % N ← N = 节点总数
- 扩容/缩容时 N 改变 → 几乎全部数据的映射结果改变
- 引发大规模数据迁移,成本高、风险大
方案二:哈希槽(Hash Slot)
在 key 与节点之间引入一层固定长度的数组:
hash(key) % 16384 = 槽位号(固定不变)
| 属性 | 值 |
|---|---|
| 槽总数 | 16384(0 ~ 16383) |
| 槽分配方式 | 每个节点负责一段连续槽位 |
| 示例 | A: 0 |
扩容优势:只需迁移受影响槽位中的数据,而非全部数据。
客户端路由与重定向
客户端如何找到数据?
客户端 ──→ 随机连接任一节点
│
▼
计算 hash(key) % 16384
│
┌─────┴─────┐
▼ ▼
槽位归自己 槽位归他人
│ │
直接执行 返回 MOVED 重定向
│ │
▼ ▼
完成 客户端重连正确节点
cluster slots命令可获取最新槽位分配信息- 主流客户端库已内置重定向逻辑,无需手动实现
数据迁移的一致性保证
迁移中的查询问题
扩容时槽位正在从旧节点 → 新节点迁移,此时查询可能落空。
解决方案:迁移状态标记
每个哈希槽维护一个「是否正在迁移」标志
旧节点收到查询:
├─ 数据存在 → 直接返回
└─ 数据不存在 → 检查槽位状态
├─ 非迁移中 → 返回「key 不存在」
└─ 迁移中 → 返回 ASK 重定向 → 客户端去新节点查询
最终架构
集群模式 = 数据分片 + 高可用
┌──────────────┐
│ 客户端 │
└──────┬───────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 主 A │ │ 主 B │ │ 主 C │
│ 0 ~ 5460 │ │5461~10922│ │10923~... │
├──────────┤ ├──────────┤ ├──────────┤
│ 从 A1 │ │ 从 B1 │ │ 从 C1 │
│ 从 A2 │ │ 从 B2 │ │ 从 C2 │
└──────────┘ └──────────┘ └──────────┘
三大特性
| 特性 | 说明 |
|---|---|
| 高可用 | 主节点宕机,从节点自动接管 |
| 高可扩展 | 数据分散多节点,支持水平扩容 |
| 高性能 | 读写分散突破单机瓶颈 |
工作示例
3 个主节点:A(0-5460)、B(5461-10922)、C(10923-16383)
写入 set user:1001 "Katrina"
hash("user:1001") % 16384 = 1234
1234 ∈ [0, 5460] → 节点 A 直接写入
5462 ∈ [0, 5460]? → 否 → 节点 A 返回 MOVED → 重定向到节点 B
读取 get user:1001
hash("user:1001") % 16384 = 1234
1234 ∈ [0, 5460] → 节点 A 直接返回 "Katrina"
5462 ∈ [0, 5460]? → 否 → 节点 A 返回 MOVED → 重定向到节点 B 读取