搭建

image-20260518171422048

主从复制模式

架构:1 个 Master + N 个 Slave,Master 负责读写,Slave 只读并异步复制 Master 数据

核心原理

  • 全量同步:Slave 首次连接或主从断连过久,Master 执行 BGSAVE生成 ==RDB== 发给 Slave,再补发复制积压缓冲区写命令

    复制积压缓冲区:如果是主节点写的时候还会把命令写到这个缓冲区里,用于快照RDB之后的数据同步

  • 增量同步(PSYNC):正常连接后,Master 将写命令异步发送给 Slave(使用复制积压缓冲区)。

  • 复制是异步的,Slave 数据存在延迟,有短暂不一致。

优缺点

  • ✅ 数据冗余(备份)、可读写分离减轻 Master 读压力。
  • Master 宕机需手动切换(人工 SLAVEOF NO ONE),恢复时间长。
  • ❌ 写能力和容量仍受限于单机 Master。

哨兵模式(Sentinel)

架构:在主从复制基础上,部署 Sentinel 集群(通常 ≥3 个奇数节点),专门负责监控、选主、通知客户端。

1
2
3
4
┌─ Sentinel1
Client → │ Sentinel2 ←→ 监控 ←→ Master → Slave1
└─ Sentinel3 ↓
Slave2

Sentinel 三大功能

  1. 监控(Monitoring):Sentinel 定期向 Master/Slave 发送 PING,判断是否下线。
  2. 自动故障转移(Automatic Failover)
    • Master 被判定 主观下线(SDOWN) → 多数 Sentinel 确认 客观下线(ODOWN)
    • Sentinel 选举 Leader(Raft-like 算法),由 Leader 从 Slave 中选新 Master(优先级→复制偏移量→runid)。
    • 通知其余 Slave 与新 Master 同步,并更新自身配置。
  3. 配置提供者(Configuration Provider):客户端连接 Sentinel,询问当前 Master 地址,实现服务发现

关键参数

  • quorum:认定 ODOWN 所需 Sentinel 同意数(通常设为 节点数//2 + 1,如 3 节点设 2)。
  • down-after-milliseconds:多久无响应判主观下线(通常 5000~30000ms)。

优缺点

  • 自动故障转移,高可用,秒级恢复(通常 10~30 秒)。
  • ✅ 兼容旧客户端(通过 Sentinel 获取 Master 地址)。
  • 仍无数据分片,写/存储容量受限于单 Master 内存。

数据丢失情况

两种数据丢失的场景,主从和哨兵都一样:

  1. 异步复制丢失(Master 宕机),未同步数据随 Master 内存消失

    没办法避免

  2. 脑裂双主写入覆盖丢失

    当 Master 连接的 healthy Slave 数不足时,Master 停止接受写请求

为什么是哨兵?

为什么单独弄个哨兵来竞选,不用slave?

Sentinel 模式不让 Slave 自竞选,主要是因为 Slave 没有全局视角来判断 Master 是真宕还是网络分区,也没有多数派投票机制防止多 Slave 同时自封 Master 造成脑裂。此外客户端需要通过 Sentinel 获取新 Master 地址,Slave 无法承担这个配置中心角色。

分片集群模式

架构去中心化的分布式架构,通常由 N 个 Master(N≥3)+ 各自的 Slave 组成,数据通过哈希槽(Hash Slot)分片存储。

1
2
3
4
5
6
Client
├── Master A (Slots 0–5460) ← Slave A1
├── Master B (Slots 5461–10922) ← Slave B1
└── Master C (Slots 10923–16383) ← Slave C1

节点间通过 Gossip 协议互连,交换槽位与节点状态

核心机制

  • 16384 个哈希槽slot = CRC16(key) % 16384,每个 Master 负责一部分槽位。
  • 客户端智能路由:客户端缓存槽位→节点映射;请求发到 wrong node 时返回 -MOVED(重定向)或 -ASK(迁移中)。
  • 内置高可用:每个分片是主从结构,主节点 fail 后经集群的所有Master节点投票,其 Slave 自动晋升为新主(==无需 Sentinel==)
  • 动态扩缩容:通过 redis-cli --cluster reshard迁移槽位(在线迁移,有短暂 ASK 重定向)。

分片下的使用问题

  1. 为什么批量命令(MGET/MSET)失效?

    协议限制:Redis 的命令执行是在单个节点内完成的。MGET要求一次性返回所有值,如果数据不在一块,无法保证原子性和一致性

  2. Pipeline,如果发现这些 Key 属于不同的 Slot/Node,客户端会自动拆分这个 Pipeline:属于 Node A 的命令打包发一次,属于 Node B 的命令打包发一次

  3. 为什么 Lua 脚本会失效?

    Lua 脚本是服务器端原子执行的。当你执行 EVAL script 2 k1 k2时,这个脚本会被发送到 k1所在的节点执行

可以使用HashTag来保证在同一个key:{batch_001}:order:success:1001

==路由视角:当这个 Key 被发送到 Cluster 时,Redis 会解析这个字符串,发现里面有 {},于是提取 batch_001来计算 Slot。但是,计算完 Slot 后,这个 Key 的名字不会改变,依然带着 {}存进去==

流程

第一步:主观下线(PFAIL)

当一个 Master 节点 A 发现另一个 Master 节点 B 在 cluster-node-timeout时间内无法连通时,A 会将 B 标记为 PFAIL(Possible Fail)

第二步:通过 Gossip 传播

节点 A 会在 Gossip 协议中把这个 PFAIL消息广播给其他节点。

第三步:客观下线(FAIL)

如果一个节点收集到集群中超过半数(Majority)的 Master 节点都认为 B 是 PFAIL,那么这些节点会将 B 标记为 FAIL(客观下线)

例如,集群有 5 个 Master,那么至少需要 3 个 Master 认为 B 挂了,B 才算 FAIL。

第四步:投票选举新 Master(Failover)

B 被标记为 FAIL 后,B 的 Slave 们会发起竞选:

  1. 延时竞选:Slave 会根据复制偏移量(谁的数据最新)和 Rank 计算出一个随机延时,数据越新、Rank 越小,延时越短,越先发起选举。
  2. 拉票:Slave 向集群中所有 Master 发送 FAILOVER_AUTH_REQUEST
  3. 投票:Master 收到请求后,如果还没有投过票(每个 Epoch 周期内只能投一票),且确认 B 确实是 FAIL,就会回复 FAILOVER_AUTH_ACK
  4. 当选:Slave 如果收到超过半数 Master 的 ACK,它就正式晋升为新的 Master。
  5. 广播:新 Master 广播 PONG消息,告知集群自己接管了哪些 Slot。

优缺点

  • 水平扩展:突破单机内存/写瓶颈,多 Master 并行写入。
  • ✅ 内置高可用,无单点(去中心化)。
  • ❌ 客户端必须支持 Cluster 协议(Jedis / Lettuce 均支持)。
  • ❌ 运维复杂,扩缩容需注意大 Key 迁移阻塞。

问题

==脑裂:==

主节点与从节点网络断开,客户端与主节点正常通信,从节点选取新的主节点,网络恢复就出现了两个主节点;==网络恢复后旧 Master 变 Slave 被新的做全量同步,分区期间写入旧 Master 的数据就丢失了==

解决:

  1. 哨兵:当 Master 连接的 healthy Slave 数不足时,Master 停止接受写请求【此时“写不可用,读仍可用”,不是宕机,而是主动降级为只读模式】,避免旧 Master 继续写入被覆盖
  2. Redis Cluster :通过多数派投票选主降低了脑裂概率,但因是异步复制,极端情况下仍可能丢失少量未同步数据