欢迎光临
我们一直在努力

04-02-A-Redis持久化与高可用详解

04-02-A-Redis持久化与高可用详解

📌 导读:Redis 持久化与高可用:RDB、AOF、主从、哨兵、Cluster。本篇回答两个核心问题:Redis 重启数据怎么不丢?Redis 挂了怎么自动恢复?

读完本文你将掌握:

  • 能配置 RDB/AOF 混合持久化,让 Redis 重启后数据不丢、恢复又快又完整
  • 能搭建主从复制并调优 repl-backlog-size,用 info replication 排查主从延迟
  • 能按「场景一/二/三」的排查套路,定位主从延迟、全量同步频繁触发、脑裂数据丢失三类故障
  • 能部署 3 哨兵 + 1 主 2 从,让主库宕机后自动完成故障转移
  • 能规划 Cluster 的 16384 个槽位分片,并用 min-replicas 配置防脑裂
  • 能根据数据量与 QPS 选型单机、主从、哨兵或 Cluster 部署形态

从 RDB/AOF 的原理与配置,到主从复制的同步流程,再到哨兵故障转移、Cluster 分片与脑裂防护,全部配流程图与配置举例。


📑 目录

  • 04-02-A-Redis持久化与高可用详解
    • 一、持久化:RDB 与 AOF
      • 1.1 为什么需要持久化
      • 1.2 RDB(快照)
      • 1.3 AOF(追加日志)
      • 1.4 RDB vs AOF 对比与选型
    • 二、主从复制
      • 2.1 为什么需要主从
      • 2.2 全量同步流程
      • 2.3 增量同步(断线重连)
      • 2.4 主从配置
      • 2.5 主从复制的坑
      • 2.6 故障排查实战
        • 场景一:主从延迟排查
        • 场景二:全量同步频繁触发排查
        • 场景三:脑裂后数据丢失恢复
    • 三、哨兵(Sentinel)
      • 3.1 哨兵三大职责
      • 3.2 故障转移流程
      • 3.3 哨兵配置
      • 3.4 哨兵故障转移实战排查
    • 四、Redis Cluster
      • 4.1 数据分片:16384 个槽
      • 4.2 Cluster 关键约束
      • 4.3 客户端重定向
      • 4.4 Cluster 搭建与运维命令
      • 4.5 脑裂防护
      • 4.6 Cluster 脑裂故障排查实战
    • 五、部署形态选型
    • 六、总结

一、持久化:RDB 与 AOF

1.1 为什么需要持久化

Redis 数据在内存中,进程重启或机器宕机数据就没了。持久化就是把内存数据定期或实时写到磁盘。

1.2 RDB(快照)

原理:fork 子进程,把当前内存数据序列化成二进制快照文件(dump.rdb)。

触发方式:

方式说明
SAVE 同步保存,阻塞主线程(生产禁用)
BGSAVE 异步保存,fork 子进程
配置触发 save 900 1 等规则自动触发

RDB 配置:

save 900 1 # 900 秒内至少 1 次修改
save 300 10 # 300 秒内至少 10 次修改
save 60 10000 # 60 秒内至少 10000 次修改
dbfilename dump.rdb
dir /var/lib/redis

优缺点:

优点缺点
文件紧凑,恢复快 两次快照之间的数据会丢
fork 子进程,不阻塞主线程 写时复制最坏情况内存翻倍,fork 大实例有毫秒级卡顿
适合备份、灾备 大数据量时 BGSAVE 耗时长

1.3 AOF(追加日志)

原理:每条写命令追加到 AOF 文件,重启时重放命令恢复数据。

刷盘策略:

策略说明数据安全性
always 每条命令都 fsync 最高,性能最差
everysec 每秒 fsync(推荐) 最多丢 1 秒
no 由操作系统决定 最低

AOF 配置:

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
auto-aof-rewrite-percentage 100 # 增长 100% 触发重写
auto-aof-rewrite-min-size 64mb
aof-use-rdb-preamble yes # 混合持久化

AOF 重写:AOF 文件越来越大时,fork 子进程把当前内存数据生成新 AOF(只保留最终状态),替换旧文件。


1.4 RDB vs AOF 对比与选型

#mermaid-svg-CR3DiTtz9MQfeGeK{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CR3DiTtz9MQfeGeK .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-CR3DiTtz9MQfeGeK .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CR3DiTtz9MQfeGeK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CR3DiTtz9MQfeGeK .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-CR3DiTtz9MQfeGeK .marker.cross{stroke:#0b0b0b;}#mermaid-svg-CR3DiTtz9MQfeGeK svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-CR3DiTtz9MQfeGeK p{margin:0;}#mermaid-svg-CR3DiTtz9MQfeGeK .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-CR3DiTtz9MQfeGeK .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-CR3DiTtz9MQfeGeK .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-CR3DiTtz9MQfeGeK .cluster-label span p{background-color:transparent;}#mermaid-svg-CR3DiTtz9MQfeGeK .label text,#mermaid-svg-CR3DiTtz9MQfeGeK span{fill:#333;color:#333;}#mermaid-svg-CR3DiTtz9MQfeGeK .node rect,#mermaid-svg-CR3DiTtz9MQfeGeK .node circle,#mermaid-svg-CR3DiTtz9MQfeGeK .node ellipse,#mermaid-svg-CR3DiTtz9MQfeGeK .node polygon,#mermaid-svg-CR3DiTtz9MQfeGeK .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-CR3DiTtz9MQfeGeK .rough-node .label text,#mermaid-svg-CR3DiTtz9MQfeGeK .node .label text,#mermaid-svg-CR3DiTtz9MQfeGeK .image-shape .label,#mermaid-svg-CR3DiTtz9MQfeGeK .icon-shape .label{text-anchor:middle;}#mermaid-svg-CR3DiTtz9MQfeGeK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CR3DiTtz9MQfeGeK .rough-node .label,#mermaid-svg-CR3DiTtz9MQfeGeK .node .label,#mermaid-svg-CR3DiTtz9MQfeGeK .image-shape .label,#mermaid-svg-CR3DiTtz9MQfeGeK .icon-shape .label{text-align:center;}#mermaid-svg-CR3DiTtz9MQfeGeK .node.clickable{cursor:pointer;}#mermaid-svg-CR3DiTtz9MQfeGeK .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-CR3DiTtz9MQfeGeK .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-CR3DiTtz9MQfeGeK .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-CR3DiTtz9MQfeGeK .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-CR3DiTtz9MQfeGeK .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-CR3DiTtz9MQfeGeK .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-CR3DiTtz9MQfeGeK .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-CR3DiTtz9MQfeGeK .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-CR3DiTtz9MQfeGeK .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-CR3DiTtz9MQfeGeK .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-CR3DiTtz9MQfeGeK .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-CR3DiTtz9MQfeGeK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CR3DiTtz9MQfeGeK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CR3DiTtz9MQfeGeK rect.text{fill:none;stroke-width:0;}#mermaid-svg-CR3DiTtz9MQfeGeK .icon-shape,#mermaid-svg-CR3DiTtz9MQfeGeK .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-CR3DiTtz9MQfeGeK .icon-shape p,#mermaid-svg-CR3DiTtz9MQfeGeK .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-CR3DiTtz9MQfeGeK .icon-shape .label rect,#mermaid-svg-CR3DiTtz9MQfeGeK .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-CR3DiTtz9MQfeGeK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CR3DiTtz9MQfeGeK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CR3DiTtz9MQfeGeK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

持久化策略选择。

允许丢几分钟数据
追求恢复速度。

选 RDB
save 900 1
save 300 10

最多丢 1 秒
追求数据完整。

选 AOF
appendfsync everysec

既要快又要完整
生产推荐。

RDB + AOF 混合
aof-use-rdb-preamble yes

维度RDBAOF
格式 二进制快照 追加命令日志
恢复速度 慢(重放命令)
数据完整性 可能丢最后一次快照后的数据 最多丢 1 秒(everysec)
文件大小 小(压缩) 大(可重写压缩)

二、主从复制

2.1 为什么需要主从

  • 高可用:主库挂了,从库顶上
  • 读扩展:读请求分流到从库
  • 数据备份:从库就是一份实时备份

2.2 全量同步流程

#mermaid-svg-ehOZjbDnX6E4qaD8{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ehOZjbDnX6E4qaD8 .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-ehOZjbDnX6E4qaD8 .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ehOZjbDnX6E4qaD8 .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-ehOZjbDnX6E4qaD8 .marker.cross{stroke:#0b0b0b;}#mermaid-svg-ehOZjbDnX6E4qaD8 svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-ehOZjbDnX6E4qaD8 p{margin:0;}#mermaid-svg-ehOZjbDnX6E4qaD8 .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-ehOZjbDnX6E4qaD8 .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-ehOZjbDnX6E4qaD8 .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-ehOZjbDnX6E4qaD8 .cluster-label span p{background-color:transparent;}#mermaid-svg-ehOZjbDnX6E4qaD8 .label text,#mermaid-svg-ehOZjbDnX6E4qaD8 span{fill:#333;color:#333;}#mermaid-svg-ehOZjbDnX6E4qaD8 .node rect,#mermaid-svg-ehOZjbDnX6E4qaD8 .node circle,#mermaid-svg-ehOZjbDnX6E4qaD8 .node ellipse,#mermaid-svg-ehOZjbDnX6E4qaD8 .node polygon,#mermaid-svg-ehOZjbDnX6E4qaD8 .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .rough-node .label text,#mermaid-svg-ehOZjbDnX6E4qaD8 .node .label text,#mermaid-svg-ehOZjbDnX6E4qaD8 .image-shape .label,#mermaid-svg-ehOZjbDnX6E4qaD8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ehOZjbDnX6E4qaD8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .rough-node .label,#mermaid-svg-ehOZjbDnX6E4qaD8 .node .label,#mermaid-svg-ehOZjbDnX6E4qaD8 .image-shape .label,#mermaid-svg-ehOZjbDnX6E4qaD8 .icon-shape .label{text-align:center;}#mermaid-svg-ehOZjbDnX6E4qaD8 .node.clickable{cursor:pointer;}#mermaid-svg-ehOZjbDnX6E4qaD8 .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-ehOZjbDnX6E4qaD8 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-ehOZjbDnX6E4qaD8 .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-ehOZjbDnX6E4qaD8 .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-ehOZjbDnX6E4qaD8 .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-ehOZjbDnX6E4qaD8 .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-ehOZjbDnX6E4qaD8 .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-ehOZjbDnX6E4qaD8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ehOZjbDnX6E4qaD8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ehOZjbDnX6E4qaD8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ehOZjbDnX6E4qaD8 .icon-shape,#mermaid-svg-ehOZjbDnX6E4qaD8 .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-ehOZjbDnX6E4qaD8 .icon-shape p,#mermaid-svg-ehOZjbDnX6E4qaD8 .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-ehOZjbDnX6E4qaD8 .icon-shape .label rect,#mermaid-svg-ehOZjbDnX6E4qaD8 .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-ehOZjbDnX6E4qaD8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ehOZjbDnX6E4qaD8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ehOZjbDnX6E4qaD8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

哨兵故障转移。

① 哨兵发现主库主观下线 SDOWN。

② 多数哨兵确认客观下线 ODOWN。

③ 哨兵间 Raft选举 Leader。

④ Leader 选新主:优先级 / offset / runid。

⑤ 通知从库复制新主。

⑥ 通知客户端新主地址。

2.3 增量同步(断线重连)

  • 从库记录 replid + offset
  • 重连时发送 PSYNC replid offset
  • 主库 repl_backlog 缓冲区包含该 offset 之后的命令 → 增量同步
  • 不包含 → 全量同步

2.4 主从配置

# 从库配置
replicaof 192.168.1.10 6379
replica-read-only yes # 从库只读
repl-backlog-size 64mb # 复制积压缓冲区,生产建议调大(默认 1mb 太小,断线重连易触发全量同步)

docker-compose 一键搭建 1 主 2 从:

version: "3.8"

services:
redis-master:
image: redis:7.2
container_name: redismaster
ports:
"6379:6379"
command: ["redis-server", "–appendonly", "yes"]

redis-slave-1:
image: redis:7.2
container_name: redisslave1
ports:
"6380:6379"
command: ["redis-server", "–replicaof", "redis-master", "6379", "–replica-read-only", "yes"]
depends_on:
redismaster

redis-slave-2:
image: redis:7.2
container_name: redisslave2
ports:
"6381:6379"
command: ["redis-server", "–replicaof", "redis-master", "6379", "–replica-read-only", "yes"]
depends_on:
redismaster

主从通过 –replicaof redis-master 6379 建立复制关系(容器内用服务名 redis-master 解析主库地址);从库通过 –replica-read-only yes 设为只读,防止误写导致主从数据不一致。启动后可用 docker compose up -d 拉起,用 redis-cli -p 6380 info replication 验证从库 master_link_status:up。

2.5 主从复制的坑

问题原因解决
主从延迟 从库单线程回放 + 网络 控制大 key、拆分批量写
全量同步阻塞 BGSAVE fork 阻塞主库 控制内存碎片、THP 设 never
脑裂 网络分区后旧主恢复 min-replicas-to-write 1

2.6 故障排查实战

场景一:主从延迟排查

现象:从库读到的数据明显滞后于主库。

排查步骤:

  • 查看主从复制状态,关注 master_repl_offset 与 slave_repl_offset 的差值:
  • redis-cli -h 192.168.1.11 -p 6379 info replication

    输出示例:

    # Replication
    role:slave
    master_host:192.168.1.10
    master_port:6379
    master_link_status:up
    master_last_io_seconds_ago:0
    master_sync_in_progress:0
    slave_repl_offset:102457
    slave_priority:100
    slave_read_only:1

    对比主库的 master_repl_offset,若两者差值持续增大,说明从库回放跟不上。

  • 定位大 key(大 key 会导致单条命令回放耗时过长):
  • redis-cli –bigkeys

    输出示例:

    # Scanning the entire keyspace to find biggest keys as well as
    # average sizes per key type. You can use -i 0.1 to sleep 0.1 sec
    # per 100 SCAN commands (not usually needed).

    ——– summary ——-

    Sampled 100000 keys in the keyspace!
    Total key length in bytes is 1048576 (avg len 10.49)

    Biggest string found so far '"user:10086:session"' has 52428800 bytes
    Biggest hash found so far '"order:20260923"' has 1000000 fields
    Biggest list found so far '"task:queue"' has 500000 items

    监控脚本:定期采集主库 master_repl_offset 与从库 slave_repl_offset,计算差值并输出告警日志:

    #!/bin/bash
    # redis_repl_monitor.sh —— 主从复制延迟监控脚本
    # 用法:./redis_repl_monitor.sh <主库IP> <从库IP> [阈值(默认1000)]

    MASTER_HOST="${1:-192.168.1.10}"
    SLAVE_HOST="${2:-192.168.1.11}"
    THRESHOLD="${3:-1000}"
    LOG_FILE="/var/log/redis_repl_monitor.log"

    # 采集主库 master_repl_offset
    master_offset=$(redis-cli -h "$MASTER_HOST" -p 6379 info replication \\
    | awk -F: '/^master_repl_offset/{gsub("\\r","");print $2}')

    # 采集从库 slave_repl_offset
    slave_offset=$(redis-cli -h "$SLAVE_HOST" -p 6379 info replication \\
    | awk -F: '/^slave_repl_offset/{gsub("\\r","");print $2}')

    # 计算差值(取绝对值)
    lag=$(( master_offset slave_offset ))
    [ "$lag" -lt 0 ] && lag=$(( lag ))

    now=$(date '+%Y-%m-%d %H:%M:%S')

    if [ "$lag" -gt "$THRESHOLD" ]; then
    echo "[$now] [ALERT] 主从延迟过大:master=$master_offset slave=$slave_offset lag=$lag (阈值=$THRESHOLD)" >> "$LOG_FILE"
    echo "[$now] [ALERT] 主从延迟过大:lag=$lag"
    else
    echo "[$now] [OK] 主从同步正常:master=$master_offset slave=$slave_offset lag=$lag" >> "$LOG_FILE"
    fi

    crontab 定时执行(每 30 秒采集一次,阈值 1000):

    * * * * * /opt/scripts/redis_repl_monitor.sh 192.168.1.10 192.168.1.11 1000 >> /var/log/redis_repl_monitor.log 2>&1
    * * * * * sleep 30; /opt/scripts/redis_repl_monitor.sh 192.168.1.10 192.168.1.11 1000 >> /var/log/redis_repl_monitor.log 2>&1

    脚本需先 chmod +x redis_repl_monitor.sh,并确保执行用户对 Redis 有 info replication 权限;告警阈值可按业务容忍度调整,延迟持续增大时结合 –bigkeys 定位大 key。

    解决:对大 key 拆分(如 hash 按字段分片)、控制单次批量写入量、必要时提升从库硬件。

    场景二:全量同步频繁触发排查

    现象:主库频繁执行 BGSAVE,从库反复全量同步,主库 CPU/内存飙升。

    排查步骤:

  • 查看 repl_backlog 大小与当前积压情况:
  • redis-cli -p 6379 info replication | grep backlog

    输出示例:

    repl_backlog_active:1
    repl_backlog_size:67108864
    repl_backlog_first_byte_offset:99821
    repl_backlog_histlen:2636

    repl_backlog_histlen 远小于 repl_backlog_size 时,说明缓冲区充足;若从库断线期间写入量超过 repl_backlog_size,就会退化为全量同步。

  • 查看主库最近是否频繁 BGSAVE:
  • redis-cli -p 6379 info stats | grep rdb_changes_since_last_save
    redis-cli -p 6379 info persistence | grep rdb_bgsave_in_progress

    输出示例:

    rdb_changes_since_last_save:0
    rdb_bgsave_in_progress:0

  • 检查网络抖动:从库 master_last_io_seconds_ago 若经常大于阈值,说明主从网络不稳定。
  • 解决:调大 repl-backlog-size(建议 256mb~1gb),排查网络抖动(交换机、带宽、TCP 重传),避免从库频繁断线重连。

    场景三:脑裂后数据丢失恢复

    现象:网络分区期间旧主仍在接受写入,分区恢复后旧主降级为从库,期间写入的数据丢失。

    排查步骤:

  • 检查 min-replicas 配置是否生效:
  • redis-cli -p 6379 config get min-replicas-to-write min-replicas-max-lag

    输出示例:

    1) "min-replicas-to-write"
    2) "1"
    3) "min-replicas-max-lag"
    4) "10"

    若 min-replicas-to-write 为 0,说明脑裂期间主库会无条件接受写入,数据丢失风险极高。

  • 查看从库数量与最近同步状态:
  • redis-cli -p 6379 info replication | grep -E "connected_slaves|slave[0-9]"

    输出示例:

    connected_slaves:2
    slave0:ip=192.168.1.11,port=6379,state=online,offset=102457,lag=0
    slave1:ip=192.168.1.12,port=6379,state=online,offset=102457,lag=0

  • 若旧主数据已落后,手动切换主从恢复:
  • # 在旧主上执行,降级为从库并指向新主
    redis-cli -p 6379 replicaof 192.168.1.11 6379

    # 确认复制状态
    redis-cli -p 6379 info replication | grep master_link_status

    输出示例:

    master_link_status:up

    解决:开启 min-replicas-to-write 1 + min-replicas-max-lag 10,脑裂期间旧主写不进去,从数据源头上避免丢失;已丢失的数据需结合 AOF/RDB 备份或业务日志补偿。


    三、哨兵(Sentinel)

    3.1 哨兵三大职责

    #mermaid-svg-kcrOLZg6sviHoKjD{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kcrOLZg6sviHoKjD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kcrOLZg6sviHoKjD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kcrOLZg6sviHoKjD .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-kcrOLZg6sviHoKjD .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-kcrOLZg6sviHoKjD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kcrOLZg6sviHoKjD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kcrOLZg6sviHoKjD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kcrOLZg6sviHoKjD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kcrOLZg6sviHoKjD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kcrOLZg6sviHoKjD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kcrOLZg6sviHoKjD .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-kcrOLZg6sviHoKjD .marker.cross{stroke:#0b0b0b;}#mermaid-svg-kcrOLZg6sviHoKjD svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-kcrOLZg6sviHoKjD p{margin:0;}#mermaid-svg-kcrOLZg6sviHoKjD .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-kcrOLZg6sviHoKjD .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-kcrOLZg6sviHoKjD .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-kcrOLZg6sviHoKjD .cluster-label span p{background-color:transparent;}#mermaid-svg-kcrOLZg6sviHoKjD .label text,#mermaid-svg-kcrOLZg6sviHoKjD span{fill:#333;color:#333;}#mermaid-svg-kcrOLZg6sviHoKjD .node rect,#mermaid-svg-kcrOLZg6sviHoKjD .node circle,#mermaid-svg-kcrOLZg6sviHoKjD .node ellipse,#mermaid-svg-kcrOLZg6sviHoKjD .node polygon,#mermaid-svg-kcrOLZg6sviHoKjD .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-kcrOLZg6sviHoKjD .rough-node .label text,#mermaid-svg-kcrOLZg6sviHoKjD .node .label text,#mermaid-svg-kcrOLZg6sviHoKjD .image-shape .label,#mermaid-svg-kcrOLZg6sviHoKjD .icon-shape .label{text-anchor:middle;}#mermaid-svg-kcrOLZg6sviHoKjD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kcrOLZg6sviHoKjD .rough-node .label,#mermaid-svg-kcrOLZg6sviHoKjD .node .label,#mermaid-svg-kcrOLZg6sviHoKjD .image-shape .label,#mermaid-svg-kcrOLZg6sviHoKjD .icon-shape .label{text-align:center;}#mermaid-svg-kcrOLZg6sviHoKjD .node.clickable{cursor:pointer;}#mermaid-svg-kcrOLZg6sviHoKjD .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-kcrOLZg6sviHoKjD .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-kcrOLZg6sviHoKjD .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-kcrOLZg6sviHoKjD .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-kcrOLZg6sviHoKjD .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-kcrOLZg6sviHoKjD .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-kcrOLZg6sviHoKjD .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-kcrOLZg6sviHoKjD .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-kcrOLZg6sviHoKjD .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-kcrOLZg6sviHoKjD .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-kcrOLZg6sviHoKjD .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-kcrOLZg6sviHoKjD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kcrOLZg6sviHoKjD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kcrOLZg6sviHoKjD rect.text{fill:none;stroke-width:0;}#mermaid-svg-kcrOLZg6sviHoKjD .icon-shape,#mermaid-svg-kcrOLZg6sviHoKjD .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-kcrOLZg6sviHoKjD .icon-shape p,#mermaid-svg-kcrOLZg6sviHoKjD .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-kcrOLZg6sviHoKjD .icon-shape .label rect,#mermaid-svg-kcrOLZg6sviHoKjD .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-kcrOLZg6sviHoKjD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kcrOLZg6sviHoKjD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kcrOLZg6sviHoKjD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    哨兵三大职责。

    ① 监控
    每秒 PING 主从和哨兵。

    ② 自动故障转移
    主库挂了选举新主。

    ③ 通知
    把新主地址告诉客户端。

    3.2 故障转移流程

    #mermaid-svg-6Tf1kjgYlpWhRGz0{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .marker.cross{stroke:#0b0b0b;}#mermaid-svg-6Tf1kjgYlpWhRGz0 svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 p{margin:0;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .cluster-label span p{background-color:transparent;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .label text,#mermaid-svg-6Tf1kjgYlpWhRGz0 span{fill:#333;color:#333;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .node rect,#mermaid-svg-6Tf1kjgYlpWhRGz0 .node circle,#mermaid-svg-6Tf1kjgYlpWhRGz0 .node ellipse,#mermaid-svg-6Tf1kjgYlpWhRGz0 .node polygon,#mermaid-svg-6Tf1kjgYlpWhRGz0 .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .rough-node .label text,#mermaid-svg-6Tf1kjgYlpWhRGz0 .node .label text,#mermaid-svg-6Tf1kjgYlpWhRGz0 .image-shape .label,#mermaid-svg-6Tf1kjgYlpWhRGz0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .rough-node .label,#mermaid-svg-6Tf1kjgYlpWhRGz0 .node .label,#mermaid-svg-6Tf1kjgYlpWhRGz0 .image-shape .label,#mermaid-svg-6Tf1kjgYlpWhRGz0 .icon-shape .label{text-align:center;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .node.clickable{cursor:pointer;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-6Tf1kjgYlpWhRGz0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6Tf1kjgYlpWhRGz0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .icon-shape,#mermaid-svg-6Tf1kjgYlpWhRGz0 .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .icon-shape p,#mermaid-svg-6Tf1kjgYlpWhRGz0 .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .icon-shape .label rect,#mermaid-svg-6Tf1kjgYlpWhRGz0 .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-6Tf1kjgYlpWhRGz0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6Tf1kjgYlpWhRGz0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6Tf1kjgYlpWhRGz0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    哨兵故障转移。

    ① 哨兵发现主库
    主观下线 SDOWN。

    ② 多数哨兵确认
    客观下线 ODOWN。

    ③ 哨兵间 Raft
    选举 Leader。

    ④ Leader 选新主:
    优先级 / offset / runid。

    ⑤ 通知从库
    复制新主。

    ⑥ 通知客户端
    新主地址。

    3.3 哨兵配置

    # sentinel.conf
    sentinel monitor mymaster 192.168.1.10 6379 2 # 2 票同意才判定下线
    sentinel down-after-milliseconds mymaster 5000 # 5 秒无响应判定主观下线
    sentinel failover-timeout mymaster 60000 # 故障转移超时
    sentinel parallel-syncs mymaster 1 # 同时同步的从库数

    部署建议:3 哨兵 + 1 主 2 从,哨兵分布在不同机器 / 机房。

    3.4 哨兵故障转移实战排查

    1. 查看当前主库地址(确认哨兵视角下谁是主库):

    redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

    输出示例:

    1) "192.168.1.10"
    2) "6379"

    返回当前主库的 IP 与端口。若故障转移已发生,这里会返回新主库地址,用于确认哨兵是否已切换主从。

    2. 查看哨兵视角的主库状态:

    redis-cli -p 26379 sentinel master mymaster

    输出示例:

    1) "name"
    2) "mymaster"
    3) "ip"
    4) "192.168.1.10"
    5) "port"
    6) "6379"
    7) "flags"
    8) "master"
    9) "num-slaves"
    10) "2"
    11) "num-other-sentinels"
    12) "2"
    13) "config-epoch"
    14) "1"

    关注 flags(master 表示主库在线,s_down/o_down 表示主观/客观下线)、num-slaves(从库数量)与 config-epoch(故障转移代数,每次切换 +1)。

    3. 查看从库列表与优先级:

    redis-cli -p 26379 sentinel replicas mymaster

    输出示例:

    1) 1) "name"
    2) "192.168.1.11:6379"
    3) "ip"
    4) "192.168.1.11"
    5) "port"
    6) "6379"
    7) "flags"
    8) "slave"
    9) "slave-priority"
    10) "100"
    11) "master-link-status"
    12) "ok"

    slave-priority 越小优先级越高,故障转移时哨兵优先选它当新主;master-link-status 为 ok 表示该从库与主库复制正常。

    4. 模拟主库宕机,观察 SDOWN → ODOWN → failover 完整流程:

    # 模拟主库阻塞 30 秒(生产环境请勿执行)
    redis-cli -p 6379 debug sleep 30

    同时观察哨兵日志(/var/log/redis/sentinel.log):

    +sdown master mymaster 192.168.1.10 6379
    +odown master mymaster 192.168.1.10 6379 #quorum 2/2
    +try-failover master mymaster 192.168.1.10 6379
    +vote-for-leader 3a2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a 1
    +elected-leader master mymaster 192.168.1.10 6379
    +failover-state-select-slave master mymaster 192.168.1.10 6379
    +selected-slave slave 192.168.1.11:6379 192.168.1.11 6379 @ mymaster 192.168.1.10 6379
    +failover-state-send-slaveof-noone slave 192.168.1.11:6379 192.168.1.11 6379 @ mymaster 192.168.1.10 6379
    +failover-state-wait-promotion slave 192.168.1.11:6379 192.168.1.11 6379 @ mymaster 192.168.1.10 6379
    +promoted-slave slave 192.168.1.11:6379 192.168.1.11 6379 @ mymaster 192.168.1.10 6379
    +failover-state-reconf-slaves master mymaster 192.168.1.10 6379
    +slave-reconf-sent slave 192.168.1.12:6379 192.168.1.12 6379 @ mymaster 192.168.1.10 6379
    +failover-end-for-timeout master mymaster 192.168.1.10 6379
    +failover-end master mymaster 192.168.1.10 6379
    +switch-master mymaster 192.168.1.10 6379 192.168.1.11 6379

    日志中 +sdown(主观下线)→ +odown(客观下线,达到 quorum)→ +try-failover(发起故障转移)→ +selected-slave(选出新主)→ +promoted-slave(新主晋升)→ +switch-master(完成切换)即为完整流程。

    故障转移耗时统计:用 date +%s%N 记录开始与结束时间,或直接统计日志中 +sdown 到 +switch-master 的时间差:

    # 记录开始时间
    start=$(date +%s%N)

    # 模拟主库宕机
    redis-cli -p 6379 debug sleep 30

    # 等待故障转移完成,确认新主
    redis-cli -p 26379 sentinel get-master-addr-by-name mymaster

    # 记录结束时间并计算耗时(毫秒)
    end=$(date +%s%N)
    echo "故障转移耗时:$(( (end start) / 1000000 )) ms"

    输出示例:

    故障转移耗时:15230 ms

    故障转移耗时主要取决于 down-after-milliseconds(判定下线时间)+ 哨兵选举 + 从库晋升与同步时间。若耗时过长,可适当调小 down-after-milliseconds,并确保从库 slave-priority 配置合理。


    四、Redis Cluster

    4.1 数据分片:16384 个槽

    #mermaid-svg-a79WzDXwqDxTUgUY{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a79WzDXwqDxTUgUY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a79WzDXwqDxTUgUY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a79WzDXwqDxTUgUY .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-a79WzDXwqDxTUgUY .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-a79WzDXwqDxTUgUY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a79WzDXwqDxTUgUY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a79WzDXwqDxTUgUY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a79WzDXwqDxTUgUY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a79WzDXwqDxTUgUY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a79WzDXwqDxTUgUY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a79WzDXwqDxTUgUY .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-a79WzDXwqDxTUgUY .marker.cross{stroke:#0b0b0b;}#mermaid-svg-a79WzDXwqDxTUgUY svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-a79WzDXwqDxTUgUY p{margin:0;}#mermaid-svg-a79WzDXwqDxTUgUY .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-a79WzDXwqDxTUgUY .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-a79WzDXwqDxTUgUY .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-a79WzDXwqDxTUgUY .cluster-label span p{background-color:transparent;}#mermaid-svg-a79WzDXwqDxTUgUY .label text,#mermaid-svg-a79WzDXwqDxTUgUY span{fill:#333;color:#333;}#mermaid-svg-a79WzDXwqDxTUgUY .node rect,#mermaid-svg-a79WzDXwqDxTUgUY .node circle,#mermaid-svg-a79WzDXwqDxTUgUY .node ellipse,#mermaid-svg-a79WzDXwqDxTUgUY .node polygon,#mermaid-svg-a79WzDXwqDxTUgUY .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-a79WzDXwqDxTUgUY .rough-node .label text,#mermaid-svg-a79WzDXwqDxTUgUY .node .label text,#mermaid-svg-a79WzDXwqDxTUgUY .image-shape .label,#mermaid-svg-a79WzDXwqDxTUgUY .icon-shape .label{text-anchor:middle;}#mermaid-svg-a79WzDXwqDxTUgUY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a79WzDXwqDxTUgUY .rough-node .label,#mermaid-svg-a79WzDXwqDxTUgUY .node .label,#mermaid-svg-a79WzDXwqDxTUgUY .image-shape .label,#mermaid-svg-a79WzDXwqDxTUgUY .icon-shape .label{text-align:center;}#mermaid-svg-a79WzDXwqDxTUgUY .node.clickable{cursor:pointer;}#mermaid-svg-a79WzDXwqDxTUgUY .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-a79WzDXwqDxTUgUY .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-a79WzDXwqDxTUgUY .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-a79WzDXwqDxTUgUY .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-a79WzDXwqDxTUgUY .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-a79WzDXwqDxTUgUY .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-a79WzDXwqDxTUgUY .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-a79WzDXwqDxTUgUY .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-a79WzDXwqDxTUgUY .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-a79WzDXwqDxTUgUY .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-a79WzDXwqDxTUgUY .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-a79WzDXwqDxTUgUY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a79WzDXwqDxTUgUY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a79WzDXwqDxTUgUY rect.text{fill:none;stroke-width:0;}#mermaid-svg-a79WzDXwqDxTUgUY .icon-shape,#mermaid-svg-a79WzDXwqDxTUgUY .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-a79WzDXwqDxTUgUY .icon-shape p,#mermaid-svg-a79WzDXwqDxTUgUY .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-a79WzDXwqDxTUgUY .icon-shape .label rect,#mermaid-svg-a79WzDXwqDxTUgUY .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-a79WzDXwqDxTUgUY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a79WzDXwqDxTUgUY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a79WzDXwqDxTUgUY :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Cluster 数据分片。

    key 计算 CRC16,
    对 16384 取模。

    slot = CRC16(key) % 16384。

    slot 0 ~ 5460,
    → 主节点 A。

    slot 5461 ~ 10922,
    → 主节点 B。

    slot 10923 ~ 16383,
    → 主节点 C。

    4.2 Cluster 关键约束

    约束说明
    多 key 操作 必须用 {hashtag} 保证同 slot
    事务 / Lua 只能操作同 slot 的 key
    数据库 只有 db0,不支持 SELECT
    最小节点 3 主(推荐 3 主 3 从)

    4.3 客户端重定向

    • MOVED:slot 已迁移到其他节点,客户端更新路由表
    • ASK:slot 正在迁移,临时重定向到目标节点

    4.4 Cluster 搭建与运维命令

    1. 创建集群(3 主 3 从,–cluster-replicas 1 表示每个主节点配 1 个从节点):

    redis-cli –cluster create 192.168.1.10:6379 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 –cluster-replicas 1

    输出示例:

    >>> Performing hash slots allocation on 6 nodes…
    Master[0] -> Slots 0 – 5460
    Master[1] -> Slots 5461 – 10922
    Master[2] -> Slots 10923 – 16383

    一键把 6 个节点组成 3 主 3 从集群,自动完成 16384 个槽位的分配。

    2. 添加节点(把新节点加入集群):

    redis-cli –cluster add-node 192.168.1.16:6379 192.168.1.10:6379

    输出示例:

    >>> Adding node 192.168.1.16:6379 to cluster 192.168.1.10:6379
    >>> Send CLUSTER MEET to node 192.168.1.16:6379 to make it join the cluster.
    [OK] New node added correctly.

    新节点加入后默认不持有任何槽位,需配合 –cluster reshard 迁移槽位才能分担数据。

    3. 在线迁移槽位(把 500 个槽从源节点迁到目标节点):

    redis-cli –cluster reshard 192.168.1.10:6379 –cluster-from 192.168.1.10:6379 –cluster-to 192.168.1.16:6379 –cluster-slots 500 –cluster-yes

    输出示例:

    Moving slot 0 from 192.168.1.10:6379 to 192.168.1.16:6379:

    槽位在线迁移实战:下面演示把 500 个槽从源节点 192.168.1.10:6379 在线迁移到目标节点 192.168.1.16:6379 的完整过程。

    ① 迁移前确认源节点槽位分布:

    redis-cli -p 6379 cluster nodes

    输出示例:

    e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 192.168.1.10:6379@16379 myself,master – 0 0 1 connected 0-5460
    a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 192.168.1.16:6379@16379 master – 0 0 2 connected

    迁移前目标节点 192.168.1.16:6379 不持有任何槽位(connected 后无槽位范围),源节点持有 0-5460 共 5461 个槽。

    ② 执行在线迁移(把 500 个槽从源节点迁到目标节点):

    redis-cli –cluster reshard 192.168.1.10:6379 \\
    –cluster-from e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 \\
    –cluster-to a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 \\
    –cluster-slots 500 –cluster-yes

    输出示例:

    >>> Performing Cluster Check (using node 192.168.1.10:6379)
    M: e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 192.168.1.10:6379
    slots:[0-5460] (5461 slots) master
    M: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 192.168.1.16:6379
    slots:[] (0 slots) master
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots…
    >>> Check slots coverage…
    [OK] All 16384 slots covered.
    >>> Ready to move 500 slots.
    Source nodes:
    M: e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 192.168.1.10:6379
    Destination node:
    M: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 192.168.1.16:6379
    Moving slot 0 from 192.168.1.10:6379 to 192.168.1.16:6379:
    Moving slot 1 from 192.168.1.10:6379 to 192.168.1.16:6379:

    Moving slot 499 from 192.168.1.10:6379 to 192.168.1.16:6379:

    –cluster-from 与 –cluster-to 传节点 id(不是 IP:端口),–cluster-slots 500 指定迁移槽数,–cluster-yes 跳过交互确认。迁移是逐槽进行的,每个槽内的 key 会被 MIGRATE 命令搬到目标节点,期间服务不中断。

    ③ 迁移后确认槽位归属:

    redis-cli -p 6379 cluster nodes

    输出示例:

    e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 192.168.1.10:6379@16379 myself,master – 0 0 1 connected 500-5460
    a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 192.168.1.16:6379@16379 master – 0 0 2 connected 0-499

    迁移完成后,源节点槽位变为 500-5460,目标节点持有 0-499 共 500 个槽,槽位不重叠、总数仍为 16384。

    ④ 迁移期间的 ASK 重定向机制:

    迁移是逐槽进行的,某个槽在迁移过程中会处于「迁移中」状态。此时客户端访问该槽的 key,会经历如下流程:

    #mermaid-svg-vafRYEncIieXoD1M{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vafRYEncIieXoD1M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vafRYEncIieXoD1M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vafRYEncIieXoD1M .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-vafRYEncIieXoD1M .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vafRYEncIieXoD1M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vafRYEncIieXoD1M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vafRYEncIieXoD1M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vafRYEncIieXoD1M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vafRYEncIieXoD1M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vafRYEncIieXoD1M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vafRYEncIieXoD1M .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-vafRYEncIieXoD1M .marker.cross{stroke:#0b0b0b;}#mermaid-svg-vafRYEncIieXoD1M svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-vafRYEncIieXoD1M p{margin:0;}#mermaid-svg-vafRYEncIieXoD1M .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-vafRYEncIieXoD1M .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vafRYEncIieXoD1M .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vafRYEncIieXoD1M .cluster-label span p{background-color:transparent;}#mermaid-svg-vafRYEncIieXoD1M .label text,#mermaid-svg-vafRYEncIieXoD1M span{fill:#333;color:#333;}#mermaid-svg-vafRYEncIieXoD1M .node rect,#mermaid-svg-vafRYEncIieXoD1M .node circle,#mermaid-svg-vafRYEncIieXoD1M .node ellipse,#mermaid-svg-vafRYEncIieXoD1M .node polygon,#mermaid-svg-vafRYEncIieXoD1M .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-vafRYEncIieXoD1M .rough-node .label text,#mermaid-svg-vafRYEncIieXoD1M .node .label text,#mermaid-svg-vafRYEncIieXoD1M .image-shape .label,#mermaid-svg-vafRYEncIieXoD1M .icon-shape .label{text-anchor:middle;}#mermaid-svg-vafRYEncIieXoD1M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vafRYEncIieXoD1M .rough-node .label,#mermaid-svg-vafRYEncIieXoD1M .node .label,#mermaid-svg-vafRYEncIieXoD1M .image-shape .label,#mermaid-svg-vafRYEncIieXoD1M .icon-shape .label{text-align:center;}#mermaid-svg-vafRYEncIieXoD1M .node.clickable{cursor:pointer;}#mermaid-svg-vafRYEncIieXoD1M .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-vafRYEncIieXoD1M .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-vafRYEncIieXoD1M .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-vafRYEncIieXoD1M .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-vafRYEncIieXoD1M .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-vafRYEncIieXoD1M .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-vafRYEncIieXoD1M .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-vafRYEncIieXoD1M .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-vafRYEncIieXoD1M .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-vafRYEncIieXoD1M .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vafRYEncIieXoD1M .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vafRYEncIieXoD1M div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vafRYEncIieXoD1M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vafRYEncIieXoD1M rect.text{fill:none;stroke-width:0;}#mermaid-svg-vafRYEncIieXoD1M .icon-shape,#mermaid-svg-vafRYEncIieXoD1M .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-vafRYEncIieXoD1M .icon-shape p,#mermaid-svg-vafRYEncIieXoD1M .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-vafRYEncIieXoD1M .icon-shape .label rect,#mermaid-svg-vafRYEncIieXoD1M .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-vafRYEncIieXoD1M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vafRYEncIieXoD1M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vafRYEncIieXoD1M :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    客户端访问迁移中的 slot。

    ① 客户端按路由表
    访问源节点。

    ② 源节点发现
    该 slot 正在迁移。

    ③ 源节点返回
    ASK 重定向,
    + 目标节点地址。

    ④ 客户端发送
    ASKING 命令,
    再发原命令。

    ⑤ 目标节点
    执行命令并返回结果。

    客户端访问迁移中 slot 的 key 时,源节点返回:

    (error) ASK 500 192.168.1.16:6379

    ASK 与 MOVED 的区别:MOVED 表示槽位已永久迁移到目标节点,客户端应更新本地路由表,后续请求直接发往新节点;ASK 表示槽位正在迁移,只是本次请求临时重定向到目标节点,客户端不更新路由表,下次访问仍先发往源节点。收到 ASK 后,客户端需先向目标节点发送 ASKING 命令(表示「我知道这个槽在迁移,请允许我访问」),再发送原始命令,目标节点才会执行。

    ⑤ 迁移期间客户端访问示例:

    # 访问迁移中的 key(假设 user:10086 落在 slot 500)
    redis-cli -c -p 6379 get user:10086

    输出示例:

    -> Redirected to slot [500] located at 192.168.1.16:6379
    "hello"

    redis-cli -c(cluster 模式)会自动处理 ASK/MOVED 重定向:收到 ASK 时自动发送 ASKING 后重试,对客户端透明。生产环境使用支持 Cluster 的客户端(如 Jedis、Lettuce、Redisson)时,重定向由客户端库自动完成。

    4. 查看槽位分配:

    redis-cli -p 6379 cluster info

    输出示例:

    cluster_state:ok
    cluster_slots_assigned:16384
    cluster_slots_ok:16384
    cluster_known_nodes:6

    查看集群整体状态与槽位分配情况,cluster_state:ok 表示集群健康。

    5. 查看节点状态:

    redis-cli -p 6379 cluster nodes

    输出示例:

    e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 192.168.1.10:6379@16379 myself,master – 0 0 1 connected 0-5460

    查看每个节点的 id、角色、地址与负责的槽位范围,用于确认主从关系与槽位归属。

    6. docker-compose 一键搭建 3 主 3 从 Cluster:

    version: "3.8"

    services:
    redis-cluster-master-1:
    image: redis:7.2
    container_name: redisclustermaster1
    network_mode: host
    command: ["redis-server", "–port", "6379", "–cluster-enabled", "yes", "–cluster-config-file", "nodes-6379.conf", "–cluster-node-timeout", "5000", "–appendonly", "yes"]

    redis-cluster-master-2:
    image: redis:7.2
    container_name: redisclustermaster2
    network_mode: host
    command: ["redis-server", "–port", "6380", "–cluster-enabled", "yes", "–cluster-config-file", "nodes-6380.conf", "–cluster-node-timeout", "5000", "–appendonly", "yes"]

    redis-cluster-master-3:
    image: redis:7.2
    container_name: redisclustermaster3
    network_mode: host
    command: ["redis-server", "–port", "6381", "–cluster-enabled", "yes", "–cluster-config-file", "nodes-6381.conf", "–cluster-node-timeout", "5000", "–appendonly", "yes"]

    redis-cluster-slave-1:
    image: redis:7.2
    container_name: redisclusterslave1
    network_mode: host
    command: ["redis-server", "–port", "6382", "–cluster-enabled", "yes", "–cluster-config-file", "nodes-6382.conf", "–cluster-node-timeout", "5000", "–appendonly", "yes"]

    redis-cluster-slave-2:
    image: redis:7.2
    container_name: redisclusterslave2
    network_mode: host
    command: ["redis-server", "–port", "6383", "–cluster-enabled", "yes", "–cluster-config-file", "nodes-6383.conf", "–cluster-node-timeout", "5000", "–appendonly", "yes"]

    redis-cluster-slave-3:
    image: redis:7.2
    container_name: redisclusterslave3
    network_mode: host
    command: ["redis-server", "–port", "6384", "–cluster-enabled", "yes", "–cluster-config-file", "nodes-6384.conf", "–cluster-node-timeout", "5000", "–appendonly", "yes"]

    每个节点都开启 cluster-enabled yes 进入集群模式;cluster-config-file 指定集群状态文件(每个节点独立,不能共用);cluster-node-timeout 5000 表示节点间 5 秒无响应即判定疑似下线。这里使用 network_mode: host 让容器直接复用宿主机网络,避免端口映射导致集群节点间通过容器 IP 通信失败。启动后可用 docker compose up -d 拉起全部 6 个节点。

    7. 初始化集群(把 6 个节点组成 3 主 3 从,–cluster-replicas 1 表示每个主节点配 1 个从节点):

    redis-cli –cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 –cluster-replicas 1

    输出示例:

    >>> Performing hash slots allocation on 6 nodes…
    Master[0] -> Slots 0 – 5460
    Master[1] -> Slots 5461 – 10922
    Master[2] -> Slots 10923 – 16383
    Adding replica 127.0.0.1:6383 to 127.0.0.1:6379
    Adding replica 127.0.0.1:6384 to 127.0.0.1:6380
    Adding replica 127.0.0.1:6382 to 127.0.0.1:6381
    >>> Nodes configuration updated
    >>> Assign a different config epoch to each node
    >>> Sending CLUSTER MEET messages to join the cluster
    Waiting for the cluster to join

    [OK] All 16384 slots covered

    命令会自动把 16384 个槽位均分给 3 个主节点(0-5460、5461-10922、10923-16383),并为每个主节点分配 1 个从节点。执行过程中会询问是否接受槽位分配方案,输入 yes 确认即可。

    8. 验证集群状态:

    redis-cli –cluster check 127.0.0.1:6379

    输出示例:

    127.0.0.1:6379 (e7d1f2a3…) -> 0 keys | 5461 slots | 1 slaves.
    127.0.0.1:6380 (a1b2c3d4…) -> 0 keys | 5461 slots | 1 slaves.
    127.0.0.1:6381 (f4e5d6c7…) -> 0 keys | 5462 slots | 1 slaves.
    [OK] 0 keys in 3 masters.
    0.00 keys per slot on average.
    >>> Performing Cluster Check (using node 127.0.0.1:6379)
    M: e7d1f2a3… 127.0.0.1:6379
    slots:[0-5460] (5461 slots) master
    1 additional replica(s)
    M: a1b2c3d4… 127.0.0.1:6380
    slots:[5461-10922] (5461 slots) master
    1 additional replica(s)
    M: f4e5d6c7… 127.0.0.1:6381
    slots:[10923-16383] (5462 slots) master
    1 additional replica(s)
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots…
    >>> Check slots coverage…
    [OK] All 16384 slots covered.

    redis-cli –cluster check 会逐节点检查槽位分配、主从关系与槽位覆盖情况。看到 [OK] All 16384 slots covered 即表示集群健康、16384 个槽位全部有主节点负责,可以正常对外服务。

    9. Java 客户端操作 Cluster 实战:

    上面都是命令行操作,实际业务中我们通过 Java 客户端访问 Cluster。下面以 Jedis 为例,演示连接、读写、槽位重定向与故障转移感知的完整写法。

    ① 引入依赖(Maven):

    <dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.1.0</version>
    </dependency>

    ② 连接 Cluster 并读写(Jedis 会自动处理 MOVED/ASK 重定向):

    import redis.clients.jedis.JedisCluster;
    import redis.clients.jedis.HostAndPort;

    import java.util.HashSet;
    import java.util.Set;

    public class ClusterDemo {

    public static void main(String[] args) {
    // 只需提供任意一个节点的地址,Jedis 会自动拉取全集群拓扑
    Set<HostAndPort> nodes = new HashSet<>();
    nodes.add(new HostAndPort("127.0.0.1", 6379));
    nodes.add(new HostAndPort("127.0.0.1", 6380));
    nodes.add(new HostAndPort("127.0.0.1", 6381));

    try (JedisCluster cluster = new JedisCluster(nodes, 5000, 5000, 3)) {
    // 写入:key 会按 CRC16(key) % 16384 路由到对应主节点
    cluster.set("user:10086", "hello");
    cluster.set("order:20260923", "paid");

    // 读取:自动跟随槽位路由
    String user = cluster.get("user:10086");
    System.out.println("user:10086 = " + user);

    // 多 key 操作必须用 {hashtag} 保证落在同一 slot
    cluster.mset("{cart:10086}:item1", "apple", "{cart:10086}:item2", "banana");
    System.out.println("cart items = " + cluster.mget("{cart:10086}:item1", "{cart:10086}:item2"));
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    JedisCluster 构造参数依次为:节点集合、连接超时、读取超时、最大重试次数。客户端内部维护一份槽位路由表,收到 MOVED 会更新路由表并重试,收到 ASK 会先发 ASKING 再重试,对业务代码完全透明。

    ③ 槽位在线迁移期间的 Java 访问(验证 ASK 重定向对客户端透明):

    import redis.clients.jedis.JedisCluster;
    import redis.clients.jedis.HostAndPort;

    import java.util.HashSet;
    import java.util.Set;

    public class ClusterReshardAccess {

    public static void main(String[] args) throws Exception {
    Set<HostAndPort> nodes = new HashSet<>();
    nodes.add(new HostAndPort("127.0.0.1", 6379));

    try (JedisCluster cluster = new JedisCluster(nodes, 5000, 5000, 3)) {
    // 迁移前写入一批 key
    for (int i = 0; i < 1000; i++) {
    cluster.set("migrate:key:" + i, "value-" + i);
    }
    System.out.println("迁移前写入完成");

    // 模拟迁移期间持续读写(此时部分 slot 处于迁移中,会触发 ASK 重定向)
    for (int i = 0; i < 1000; i++) {
    String v = cluster.get("migrate:key:" + i);
    if (!("value-" + i).equals(v)) {
    System.out.println("数据不一致: key=" + i + " value=" + v);
    }
    }
    System.out.println("迁移期间读写全部成功,ASK 重定向对客户端透明");
    }
    }
    }

    迁移期间客户端无需任何改动:JedisCluster 收到 ASK 后自动发送 ASKING 并重试,业务代码无感知。若使用 Lettuce 或 Redisson,同样内置了重定向处理,只需在连接时传入集群节点列表即可。

    ④ 故障转移感知(主节点宕机后,客户端自动切换到新主):

    import redis.clients.jedis.JedisCluster;
    import redis.clients.jedis.HostAndPort;

    import java.util.HashSet;
    import java.util.Set;

    public class ClusterFailoverAware {

    public static void main(String[] args) throws Exception {
    Set<HostAndPort> nodes = new HashSet<>();
    nodes.add(new HostAndPort("127.0.0.1", 6379));
    nodes.add(new HostAndPort("127.0.0.1", 6380));
    nodes.add(new HostAndPort("127.0.0.1", 6381));

    try (JedisCluster cluster = new JedisCluster(nodes, 5000, 5000, 3)) {
    cluster.set("failover:demo", "before");

    // 模拟主节点宕机(生产环境请勿执行)
    // redis-cli -p 6379 debug sleep 30

    // 故障转移完成后,客户端自动感知新主,读写不受影响
    String v = cluster.get("failover:demo");
    System.out.println("故障转移后读取 = " + v);

    cluster.set("failover:demo", "after");
    System.out.println("故障转移后写入成功");
    }
    }
    }

    主节点宕机后,集群完成故障转移,JedisCluster 通过 MOVED 重定向自动感知新主地址并更新路由表,业务无需重启。建议在客户端侧开启连接池并设置合理的重试次数,以平滑度过故障转移窗口。

    ⑤ 连接池化(生产推荐,避免频繁创建连接):

    import redis.clients.jedis.JedisCluster;
    import redis.clients.jedis.HostAndPort;
    import redis.clients.jedis.JedisPoolConfig;

    import java.util.HashSet;
    import java.util.Set;

    public class ClusterPoolDemo {

    public static JedisCluster buildCluster() {
    JedisPoolConfig poolConfig = new JedisPoolConfig();
    poolConfig.setMaxTotal(200); // 最大连接数
    poolConfig.setMaxIdle(50); // 最大空闲连接
    poolConfig.setMinIdle(10); // 最小空闲连接
    poolConfig.setMaxWaitMillis(3000); // 获取连接最大等待时间

    Set<HostAndPort> nodes = new HashSet<>();
    nodes.add(new HostAndPort("127.0.0.1", 6379));
    nodes.add(new HostAndPort("127.0.0.1", 6380));
    nodes.add(new HostAndPort("127.0.0.1", 6381));

    return new JedisCluster(nodes, 5000, 5000, 3, poolConfig);
    }

    public static void main(String[] args) {
    try (JedisCluster cluster = buildCluster()) {
    cluster.set("pool:demo", "ok");
    System.out.println(cluster.get("pool:demo"));
    }
    }
    }

    生产环境务必使用连接池:JedisCluster 内部为每个节点维护一个连接池,setMaxTotal 需按节点数 × 单节点并发合理设置,避免连接数不足导致请求排队。

    4.5 脑裂防护

    # 主库至少要有 1 个从库连接才接受写
    min-replicas-to-write 1
    min-replicas-max-lag 10

    4.6 Cluster 脑裂故障排查实战

    现象:网络分区期间旧主仍在接受写入,分区恢复后旧主降级为从库,期间写入的数据丢失,且集群可能短暂进入 cluster_state:fail。

    排查步骤:

    1. 查看集群状态,确认 cluster_state:fail 的判定标准:

    redis-cli -p 6379 cluster info

    输出示例(健康状态):

    cluster_state:ok
    cluster_slots_assigned:16384
    cluster_slots_ok:16384
    cluster_slots_pfail:0
    cluster_slots_fail:0
    cluster_known_nodes:6
    cluster_size:3

    cluster_state:fail 的判定标准:当任意一个持有槽位的主节点处于 fail 状态(即该主节点不可达,且其从节点未能及时接管),或任意一个槽位处于 fail 状态(cluster_slots_fail > 0)时,集群会拒绝服务,cluster_state 变为 fail。此时客户端会收到 CLUSTERDOWN The cluster is down 错误。若只是部分槽位处于 pfail(疑似下线)状态,集群仍可对外服务,但需尽快处理。

    2. 检查主从角色是否错乱:

    redis-cli -p 6379 cluster nodes

    输出示例(脑裂后角色错乱):

    e7d1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9 192.168.1.10:6379@16379 myself,slave – 0 0 1 connected 0-5460
    a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 192.168.1.11:6379@16379 master – 0 0 2 connected 0-5460

    正常情况下,每个持有槽位的主节点应只有一个 master 角色且槽位不重叠。若发现两个节点同时以 master 角色持有同一段槽位(如上面 192.168.1.10 与 192.168.1.11 都显示 connected 0-5460),说明发生了脑裂——旧主未及时降级,新主已接管同一批槽位。此时旧主上的写入会丢失,需立即确认 min-replicas 配置并手动降级旧主。

    3. 验证 min-replicas-to-write 1 配置是否生效:

    redis-cli -p 6379 config get min-replicas-to-write min-replicas-max-lag

    输出示例:

    1) "min-replicas-to-write"
    2) "1"
    3) "min-replicas-max-lag"
    4) "10"

    配置生效后,当主库在 min-replicas-max-lag(10 秒)内没有任何从库在线时,主库会拒绝写入,返回 (error) NOREPLICAS Not enough good replicas to write.。验证方法:断开主库的所有从库连接后尝试写入:

    redis-cli -p 6379 set user:10086 "hello"

    预期输出(从库全部断开后):

    (error) NOREPLICAS Not enough good replicas to write.

    恢复从库连接后再次写入,应返回 OK,说明 min-replicas-to-write 已生效,脑裂期间旧主无法接受写入,从数据源头上避免丢失。

    4. 脑裂后数据补偿的两种方案:

    方案一:AOF 重放(旧主开启了 AOF 且脑裂期间有写入时):

    # 1. 停止旧主,避免继续写入
    redis-cli -p 6379 shutdown nosave

    # 2. 备份旧主的 AOF 文件
    cp /var/lib/redis/appendonly.aof /var/lib/redis/appendonly.aof.bak

    # 3. 用 redis-check-aof 修复并截断损坏的尾部
    redis-check-aof –fix /var/lib/redis/appendonly.aof

    # 4. 重启旧主,AOF 重放恢复脑裂期间写入的数据
    redis-server /etc/redis/redis.conf

    AOF 重放适合脑裂期间写入量不大、且旧主 AOF 文件完整的场景。注意:重放前需先确认这些数据没有与新主数据冲突,否则可能覆盖新主已写入的正确数据。

    方案二:业务日志回放(AOF 不可用或数据冲突时):

    # 1. 从业务日志中提取脑裂时间窗口内的写命令(示例:从应用日志过滤 SET/DEL 等写操作)
    grep -E "SET|DEL|HSET|LPUSH" /var/log/app/redis_write.log \\
    | awk -F'[][]' '$2 >= "2026-09-23 10:00:00" && $2 <= "2026-09-23 10:05:00"' \\
    > /tmp/split_brain_replay.txt

    # 2. 逐条回放到新主(需先确认 key 归属的 slot 在新主上)
    redis-cli -p 6379 –pipe < /tmp/split_brain_replay.txt

    # 3. 校验回放结果
    redis-cli -p 6379 mget user:10086 order:20260923

    业务日志回放适合脑裂期间写入量大、或 AOF 文件已损坏的场景。回放前务必先核对 key 的 slot 归属(用 redis-cli -p 6379 cluster keyslot user:10086),确保目标 key 由新主持有,避免回放到错误节点。

    解决:开启 min-replicas-to-write 1 + min-replicas-max-lag 10 从源头防脑裂写入;已丢失数据按「AOF 重放」或「业务日志回放」两种方案补偿,回放前先确认数据无冲突、slot 归属正确。


    五、部署形态选型

    #mermaid-svg-wjx5JCa9bOuUtjHf{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wjx5JCa9bOuUtjHf .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-wjx5JCa9bOuUtjHf .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wjx5JCa9bOuUtjHf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wjx5JCa9bOuUtjHf .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-wjx5JCa9bOuUtjHf .marker.cross{stroke:#0b0b0b;}#mermaid-svg-wjx5JCa9bOuUtjHf svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-wjx5JCa9bOuUtjHf p{margin:0;}#mermaid-svg-wjx5JCa9bOuUtjHf .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-wjx5JCa9bOuUtjHf .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-wjx5JCa9bOuUtjHf .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-wjx5JCa9bOuUtjHf .cluster-label span p{background-color:transparent;}#mermaid-svg-wjx5JCa9bOuUtjHf .label text,#mermaid-svg-wjx5JCa9bOuUtjHf span{fill:#333;color:#333;}#mermaid-svg-wjx5JCa9bOuUtjHf .node rect,#mermaid-svg-wjx5JCa9bOuUtjHf .node circle,#mermaid-svg-wjx5JCa9bOuUtjHf .node ellipse,#mermaid-svg-wjx5JCa9bOuUtjHf .node polygon,#mermaid-svg-wjx5JCa9bOuUtjHf .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-wjx5JCa9bOuUtjHf .rough-node .label text,#mermaid-svg-wjx5JCa9bOuUtjHf .node .label text,#mermaid-svg-wjx5JCa9bOuUtjHf .image-shape .label,#mermaid-svg-wjx5JCa9bOuUtjHf .icon-shape .label{text-anchor:middle;}#mermaid-svg-wjx5JCa9bOuUtjHf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wjx5JCa9bOuUtjHf .rough-node .label,#mermaid-svg-wjx5JCa9bOuUtjHf .node .label,#mermaid-svg-wjx5JCa9bOuUtjHf .image-shape .label,#mermaid-svg-wjx5JCa9bOuUtjHf .icon-shape .label{text-align:center;}#mermaid-svg-wjx5JCa9bOuUtjHf .node.clickable{cursor:pointer;}#mermaid-svg-wjx5JCa9bOuUtjHf .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-wjx5JCa9bOuUtjHf .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-wjx5JCa9bOuUtjHf .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-wjx5JCa9bOuUtjHf .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-wjx5JCa9bOuUtjHf .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-wjx5JCa9bOuUtjHf .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-wjx5JCa9bOuUtjHf .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-wjx5JCa9bOuUtjHf .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-wjx5JCa9bOuUtjHf .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-wjx5JCa9bOuUtjHf .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-wjx5JCa9bOuUtjHf .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-wjx5JCa9bOuUtjHf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wjx5JCa9bOuUtjHf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wjx5JCa9bOuUtjHf rect.text{fill:none;stroke-width:0;}#mermaid-svg-wjx5JCa9bOuUtjHf .icon-shape,#mermaid-svg-wjx5JCa9bOuUtjHf .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-wjx5JCa9bOuUtjHf .icon-shape p,#mermaid-svg-wjx5JCa9bOuUtjHf .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-wjx5JCa9bOuUtjHf .icon-shape .label rect,#mermaid-svg-wjx5JCa9bOuUtjHf .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-wjx5JCa9bOuUtjHf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wjx5JCa9bOuUtjHf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wjx5JCa9bOuUtjHf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Redis 部署选型。

    数据量 < 10GB,
    QPS < 10 万。

    单机 + 主从,
    简单可靠。

    需要自动故障转移,
    数据量 < 10GB。

    哨兵模式,
    3 哨兵 + 1 主 2 从。

    数据量 > 10GB,
    或 QPS > 10 万。

    Cluster 模式,
    3 主 3 从起步。

    形态高可用扩展性适用场景
    单机 ❌ 无扩展 开发 / 测试
    主从复制 半自动 读扩展 读多写少
    哨兵 ✅ 自动故障转移 读扩展 中小规模
    Cluster ✅ 自动故障转移 读写均可扩展 大规模

    六、总结

    #mermaid-svg-L9higWB7Ds6ItxVV{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-L9higWB7Ds6ItxVV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-L9higWB7Ds6ItxVV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-L9higWB7Ds6ItxVV .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-L9higWB7Ds6ItxVV .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-L9higWB7Ds6ItxVV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-L9higWB7Ds6ItxVV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L9higWB7Ds6ItxVV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L9higWB7Ds6ItxVV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-L9higWB7Ds6ItxVV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L9higWB7Ds6ItxVV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L9higWB7Ds6ItxVV .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-L9higWB7Ds6ItxVV .marker.cross{stroke:#0b0b0b;}#mermaid-svg-L9higWB7Ds6ItxVV svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-L9higWB7Ds6ItxVV p{margin:0;}#mermaid-svg-L9higWB7Ds6ItxVV .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-L9higWB7Ds6ItxVV .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-L9higWB7Ds6ItxVV .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-L9higWB7Ds6ItxVV .cluster-label span p{background-color:transparent;}#mermaid-svg-L9higWB7Ds6ItxVV .label text,#mermaid-svg-L9higWB7Ds6ItxVV span{fill:#333;color:#333;}#mermaid-svg-L9higWB7Ds6ItxVV .node rect,#mermaid-svg-L9higWB7Ds6ItxVV .node circle,#mermaid-svg-L9higWB7Ds6ItxVV .node ellipse,#mermaid-svg-L9higWB7Ds6ItxVV .node polygon,#mermaid-svg-L9higWB7Ds6ItxVV .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-L9higWB7Ds6ItxVV .rough-node .label text,#mermaid-svg-L9higWB7Ds6ItxVV .node .label text,#mermaid-svg-L9higWB7Ds6ItxVV .image-shape .label,#mermaid-svg-L9higWB7Ds6ItxVV .icon-shape .label{text-anchor:middle;}#mermaid-svg-L9higWB7Ds6ItxVV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-L9higWB7Ds6ItxVV .rough-node .label,#mermaid-svg-L9higWB7Ds6ItxVV .node .label,#mermaid-svg-L9higWB7Ds6ItxVV .image-shape .label,#mermaid-svg-L9higWB7Ds6ItxVV .icon-shape .label{text-align:center;}#mermaid-svg-L9higWB7Ds6ItxVV .node.clickable{cursor:pointer;}#mermaid-svg-L9higWB7Ds6ItxVV .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-L9higWB7Ds6ItxVV .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-L9higWB7Ds6ItxVV .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-L9higWB7Ds6ItxVV .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-L9higWB7Ds6ItxVV .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-L9higWB7Ds6ItxVV .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-L9higWB7Ds6ItxVV .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-L9higWB7Ds6ItxVV .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-L9higWB7Ds6ItxVV .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-L9higWB7Ds6ItxVV .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-L9higWB7Ds6ItxVV .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-L9higWB7Ds6ItxVV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-L9higWB7Ds6ItxVV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-L9higWB7Ds6ItxVV rect.text{fill:none;stroke-width:0;}#mermaid-svg-L9higWB7Ds6ItxVV .icon-shape,#mermaid-svg-L9higWB7Ds6ItxVV .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-L9higWB7Ds6ItxVV .icon-shape p,#mermaid-svg-L9higWB7Ds6ItxVV .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-L9higWB7Ds6ItxVV .icon-shape .label rect,#mermaid-svg-L9higWB7Ds6ItxVV .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-L9higWB7Ds6ItxVV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-L9higWB7Ds6ItxVV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-L9higWB7Ds6ItxVV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    持久化与高可用核心结论。

    持久化
    生产用 RDB + AOF 混合
    appendfsync everysec

    主从
    全量 + 增量同步
    控制大 key 防延迟。

    哨兵
    3 哨兵自动故障转移
    中小规模首选。

    Cluster
    16384 slot 分片
    min-replicas 防脑裂。


    📌 配套阅读: 上一篇:《04-01-A-Redis数据结构与底层实现详解.md》

    下一篇:《04-03-A-Redis缓存架构设计与治理.md》

    如果这篇文章对你有帮助,欢迎点赞、收藏、关注!🚀

    赞(0)
    未经允许不得转载:171主机测评 » 04-02-A-Redis持久化与高可用详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址