哨兵
Redis 从 2.8 开始提供了 Redis Sentinel(哨兵)用于监控 Redis 主从节点,当主节点故障时,自动选择一个从节点提升为新的主节点,实现自动故障转移。
注意一般分为两种情况:
-
从节点主动断开与主节点的连接 :Sentinel 一般不会 因此把这个从节点提升为主节点,因为这是从节点自身下线 ,主节点并没有发生故障;Sentinel 的故障转移主要针对主节点故障。
-
主节点真正挂掉 :Sentinel 检测到主节点客观下线后,会选择一个健康的从节点,将它提升为新的主节点,这就是自动故障转移。
概念
| 名词 | 逻辑结构 | 物理结构 |
|---|---|---|
| 主节点 | Redis 主服务 | 一个独立的 redis-server 进程 |
| 从节点 | Redis 从服务 | 一个独立的 redis-server 进程 |
| Redis 数据节点 | 主从节点 | 主节点和从节点的进程 |
| 哨兵节点 | 监控 Redis 数据节点的节点 | 一个独立的 redis-sentinel 进程 |
| 哨兵节点集合 | 若干哨兵节点的抽象组合 | 若干 redis-sentinel 进程 |
| Redis 哨兵(Sentinel) | Redis 提供的高可用方案 | 哨兵节点集合 + Redis 主从节点 |
| 应用方 | 泛指一个或多个客户端 | 一个或多个连接 Redis 的进程 |
主从复制的问题
主从复制可以实现数据备份和读写分离,但仍存在两个问题:
- 高可用问题 :主节点宕机后,需要人工切换从节点,故障恢复慢。→ Redis Sentinel 解决
- 扩展性问题 :只能分担读压力,写压力和存储压力仍受单机限制。→ Redis Cluster 解决
本章主要介绍 Redis Sentinel 如何解决主节点故障后的高可用问题。
人工恢复主节点故障
在主从复制模式下,主节点故障后的人工恢复流程非常繁琐,整体过程如下:
阶段1:正常运行的主从架构
#mermaid-svg-HdeGCYrsPy7eaCKU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HdeGCYrsPy7eaCKU .error-icon{fill:#552222;}#mermaid-svg-HdeGCYrsPy7eaCKU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HdeGCYrsPy7eaCKU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HdeGCYrsPy7eaCKU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HdeGCYrsPy7eaCKU .marker.cross{stroke:#333333;}#mermaid-svg-HdeGCYrsPy7eaCKU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HdeGCYrsPy7eaCKU p{margin:0;}#mermaid-svg-HdeGCYrsPy7eaCKU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU .cluster-label text{fill:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU .cluster-label span{color:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU .cluster-label span p{background-color:transparent;}#mermaid-svg-HdeGCYrsPy7eaCKU .label text,#mermaid-svg-HdeGCYrsPy7eaCKU span{fill:#333;color:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU .node rect,#mermaid-svg-HdeGCYrsPy7eaCKU .node circle,#mermaid-svg-HdeGCYrsPy7eaCKU .node ellipse,#mermaid-svg-HdeGCYrsPy7eaCKU .node polygon,#mermaid-svg-HdeGCYrsPy7eaCKU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HdeGCYrsPy7eaCKU .rough-node .label text,#mermaid-svg-HdeGCYrsPy7eaCKU .node .label text,#mermaid-svg-HdeGCYrsPy7eaCKU .image-shape .label,#mermaid-svg-HdeGCYrsPy7eaCKU .icon-shape .label{text-anchor:middle;}#mermaid-svg-HdeGCYrsPy7eaCKU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HdeGCYrsPy7eaCKU .rough-node .label,#mermaid-svg-HdeGCYrsPy7eaCKU .node .label,#mermaid-svg-HdeGCYrsPy7eaCKU .image-shape .label,#mermaid-svg-HdeGCYrsPy7eaCKU .icon-shape .label{text-align:center;}#mermaid-svg-HdeGCYrsPy7eaCKU .node.clickable{cursor:pointer;}#mermaid-svg-HdeGCYrsPy7eaCKU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HdeGCYrsPy7eaCKU .arrowheadPath{fill:#333333;}#mermaid-svg-HdeGCYrsPy7eaCKU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HdeGCYrsPy7eaCKU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HdeGCYrsPy7eaCKU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HdeGCYrsPy7eaCKU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HdeGCYrsPy7eaCKU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HdeGCYrsPy7eaCKU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HdeGCYrsPy7eaCKU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HdeGCYrsPy7eaCKU .cluster text{fill:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU .cluster span{color:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HdeGCYrsPy7eaCKU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HdeGCYrsPy7eaCKU rect.text{fill:none;stroke-width:0;}#mermaid-svg-HdeGCYrsPy7eaCKU .icon-shape,#mermaid-svg-HdeGCYrsPy7eaCKU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HdeGCYrsPy7eaCKU .icon-shape p,#mermaid-svg-HdeGCYrsPy7eaCKU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HdeGCYrsPy7eaCKU .icon-shape .label rect,#mermaid-svg-HdeGCYrsPy7eaCKU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HdeGCYrsPy7eaCKU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HdeGCYrsPy7eaCKU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HdeGCYrsPy7eaCKU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端
主从集群
主从同步
主从同步
读写命令
读写命令
读写命令
读写命令
Master
Slave 1
Slave 2
Client 1
Client 2
Client 3
Client 4
阶段2:主节点宕机,人工发现
#mermaid-svg-Kt9z6Kih5bJFW2dW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Kt9z6Kih5bJFW2dW .error-icon{fill:#552222;}#mermaid-svg-Kt9z6Kih5bJFW2dW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Kt9z6Kih5bJFW2dW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .marker.cross{stroke:#333333;}#mermaid-svg-Kt9z6Kih5bJFW2dW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Kt9z6Kih5bJFW2dW p{margin:0;}#mermaid-svg-Kt9z6Kih5bJFW2dW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .cluster-label text{fill:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .cluster-label span{color:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .cluster-label span p{background-color:transparent;}#mermaid-svg-Kt9z6Kih5bJFW2dW .label text,#mermaid-svg-Kt9z6Kih5bJFW2dW span{fill:#333;color:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .node rect,#mermaid-svg-Kt9z6Kih5bJFW2dW .node circle,#mermaid-svg-Kt9z6Kih5bJFW2dW .node ellipse,#mermaid-svg-Kt9z6Kih5bJFW2dW .node polygon,#mermaid-svg-Kt9z6Kih5bJFW2dW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .rough-node .label text,#mermaid-svg-Kt9z6Kih5bJFW2dW .node .label text,#mermaid-svg-Kt9z6Kih5bJFW2dW .image-shape .label,#mermaid-svg-Kt9z6Kih5bJFW2dW .icon-shape .label{text-anchor:middle;}#mermaid-svg-Kt9z6Kih5bJFW2dW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .rough-node .label,#mermaid-svg-Kt9z6Kih5bJFW2dW .node .label,#mermaid-svg-Kt9z6Kih5bJFW2dW .image-shape .label,#mermaid-svg-Kt9z6Kih5bJFW2dW .icon-shape .label{text-align:center;}#mermaid-svg-Kt9z6Kih5bJFW2dW .node.clickable{cursor:pointer;}#mermaid-svg-Kt9z6Kih5bJFW2dW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .arrowheadPath{fill:#333333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kt9z6Kih5bJFW2dW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Kt9z6Kih5bJFW2dW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kt9z6Kih5bJFW2dW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Kt9z6Kih5bJFW2dW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .cluster text{fill:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW .cluster span{color:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Kt9z6Kih5bJFW2dW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Kt9z6Kih5bJFW2dW rect.text{fill:none;stroke-width:0;}#mermaid-svg-Kt9z6Kih5bJFW2dW .icon-shape,#mermaid-svg-Kt9z6Kih5bJFW2dW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kt9z6Kih5bJFW2dW .icon-shape p,#mermaid-svg-Kt9z6Kih5bJFW2dW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Kt9z6Kih5bJFW2dW .icon-shape .label rect,#mermaid-svg-Kt9z6Kih5bJFW2dW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kt9z6Kih5bJFW2dW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Kt9z6Kih5bJFW2dW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Kt9z6Kih5bJFW2dW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Kt9z6Kih5bJFW2dW .fault>*{fill:red!important;color:white!important;}#mermaid-svg-Kt9z6Kih5bJFW2dW .fault span{fill:red!important;color:white!important;}#mermaid-svg-Kt9z6Kih5bJFW2dW .fault tspan{fill:white!important;} 客户端
主从集群
主从同步
主从同步
读写命令
读写命令
读写命令
读写命令
Master
宕机
Slave 1
Slave 2
Client 1
Client 2
Client 3
Client 4
用户监控发现主节点宕机
阶段3:人工选定新主节点
#mermaid-svg-zOveknRiJgFpwr7l{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zOveknRiJgFpwr7l .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zOveknRiJgFpwr7l .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zOveknRiJgFpwr7l .error-icon{fill:#552222;}#mermaid-svg-zOveknRiJgFpwr7l .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zOveknRiJgFpwr7l .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zOveknRiJgFpwr7l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zOveknRiJgFpwr7l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zOveknRiJgFpwr7l .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zOveknRiJgFpwr7l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zOveknRiJgFpwr7l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zOveknRiJgFpwr7l .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zOveknRiJgFpwr7l .marker.cross{stroke:#333333;}#mermaid-svg-zOveknRiJgFpwr7l svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zOveknRiJgFpwr7l p{margin:0;}#mermaid-svg-zOveknRiJgFpwr7l .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zOveknRiJgFpwr7l .cluster-label text{fill:#333;}#mermaid-svg-zOveknRiJgFpwr7l .cluster-label span{color:#333;}#mermaid-svg-zOveknRiJgFpwr7l .cluster-label span p{background-color:transparent;}#mermaid-svg-zOveknRiJgFpwr7l .label text,#mermaid-svg-zOveknRiJgFpwr7l span{fill:#333;color:#333;}#mermaid-svg-zOveknRiJgFpwr7l .node rect,#mermaid-svg-zOveknRiJgFpwr7l .node circle,#mermaid-svg-zOveknRiJgFpwr7l .node ellipse,#mermaid-svg-zOveknRiJgFpwr7l .node polygon,#mermaid-svg-zOveknRiJgFpwr7l .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zOveknRiJgFpwr7l .rough-node .label text,#mermaid-svg-zOveknRiJgFpwr7l .node .label text,#mermaid-svg-zOveknRiJgFpwr7l .image-shape .label,#mermaid-svg-zOveknRiJgFpwr7l .icon-shape .label{text-anchor:middle;}#mermaid-svg-zOveknRiJgFpwr7l .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zOveknRiJgFpwr7l .rough-node .label,#mermaid-svg-zOveknRiJgFpwr7l .node .label,#mermaid-svg-zOveknRiJgFpwr7l .image-shape .label,#mermaid-svg-zOveknRiJgFpwr7l .icon-shape .label{text-align:center;}#mermaid-svg-zOveknRiJgFpwr7l .node.clickable{cursor:pointer;}#mermaid-svg-zOveknRiJgFpwr7l .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zOveknRiJgFpwr7l .arrowheadPath{fill:#333333;}#mermaid-svg-zOveknRiJgFpwr7l .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zOveknRiJgFpwr7l .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zOveknRiJgFpwr7l .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zOveknRiJgFpwr7l .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zOveknRiJgFpwr7l .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zOveknRiJgFpwr7l .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zOveknRiJgFpwr7l .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zOveknRiJgFpwr7l .cluster text{fill:#333;}#mermaid-svg-zOveknRiJgFpwr7l .cluster span{color:#333;}#mermaid-svg-zOveknRiJgFpwr7l div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zOveknRiJgFpwr7l .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zOveknRiJgFpwr7l rect.text{fill:none;stroke-width:0;}#mermaid-svg-zOveknRiJgFpwr7l .icon-shape,#mermaid-svg-zOveknRiJgFpwr7l .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zOveknRiJgFpwr7l .icon-shape p,#mermaid-svg-zOveknRiJgFpwr7l .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zOveknRiJgFpwr7l .icon-shape .label rect,#mermaid-svg-zOveknRiJgFpwr7l .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zOveknRiJgFpwr7l .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zOveknRiJgFpwr7l .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zOveknRiJgFpwr7l :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Slave 1
Slave 2
用户在从节点集群中选择
一个作为新的主节点
阶段4:调整从节点同步关系
#mermaid-svg-Cgy7Sb0xUZoyKjAO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .error-icon{fill:#552222;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .marker.cross{stroke:#333333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO p{margin:0;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .cluster-label text{fill:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .cluster-label span{color:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .cluster-label span p{background-color:transparent;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .label text,#mermaid-svg-Cgy7Sb0xUZoyKjAO span{fill:#333;color:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .node rect,#mermaid-svg-Cgy7Sb0xUZoyKjAO .node circle,#mermaid-svg-Cgy7Sb0xUZoyKjAO .node ellipse,#mermaid-svg-Cgy7Sb0xUZoyKjAO .node polygon,#mermaid-svg-Cgy7Sb0xUZoyKjAO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .rough-node .label text,#mermaid-svg-Cgy7Sb0xUZoyKjAO .node .label text,#mermaid-svg-Cgy7Sb0xUZoyKjAO .image-shape .label,#mermaid-svg-Cgy7Sb0xUZoyKjAO .icon-shape .label{text-anchor:middle;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .rough-node .label,#mermaid-svg-Cgy7Sb0xUZoyKjAO .node .label,#mermaid-svg-Cgy7Sb0xUZoyKjAO .image-shape .label,#mermaid-svg-Cgy7Sb0xUZoyKjAO .icon-shape .label{text-align:center;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .node.clickable{cursor:pointer;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .arrowheadPath{fill:#333333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Cgy7Sb0xUZoyKjAO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cgy7Sb0xUZoyKjAO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Cgy7Sb0xUZoyKjAO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .cluster text{fill:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .cluster span{color:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Cgy7Sb0xUZoyKjAO rect.text{fill:none;stroke-width:0;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .icon-shape,#mermaid-svg-Cgy7Sb0xUZoyKjAO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .icon-shape p,#mermaid-svg-Cgy7Sb0xUZoyKjAO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .icon-shape .label rect,#mermaid-svg-Cgy7Sb0xUZoyKjAO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cgy7Sb0xUZoyKjAO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Cgy7Sb0xUZoyKjAO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Cgy7Sb0xUZoyKjAO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 新主从集群
主从同步
原 Slave 1
新 Master
Slave 2
配置从节点同步新的主节点
阶段5:客户端切换到新主节点
#mermaid-svg-UKHij6aXHrMnBDqb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UKHij6aXHrMnBDqb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UKHij6aXHrMnBDqb .error-icon{fill:#552222;}#mermaid-svg-UKHij6aXHrMnBDqb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UKHij6aXHrMnBDqb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UKHij6aXHrMnBDqb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UKHij6aXHrMnBDqb .marker.cross{stroke:#333333;}#mermaid-svg-UKHij6aXHrMnBDqb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UKHij6aXHrMnBDqb p{margin:0;}#mermaid-svg-UKHij6aXHrMnBDqb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UKHij6aXHrMnBDqb .cluster-label text{fill:#333;}#mermaid-svg-UKHij6aXHrMnBDqb .cluster-label span{color:#333;}#mermaid-svg-UKHij6aXHrMnBDqb .cluster-label span p{background-color:transparent;}#mermaid-svg-UKHij6aXHrMnBDqb .label text,#mermaid-svg-UKHij6aXHrMnBDqb span{fill:#333;color:#333;}#mermaid-svg-UKHij6aXHrMnBDqb .node rect,#mermaid-svg-UKHij6aXHrMnBDqb .node circle,#mermaid-svg-UKHij6aXHrMnBDqb .node ellipse,#mermaid-svg-UKHij6aXHrMnBDqb .node polygon,#mermaid-svg-UKHij6aXHrMnBDqb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UKHij6aXHrMnBDqb .rough-node .label text,#mermaid-svg-UKHij6aXHrMnBDqb .node .label text,#mermaid-svg-UKHij6aXHrMnBDqb .image-shape .label,#mermaid-svg-UKHij6aXHrMnBDqb .icon-shape .label{text-anchor:middle;}#mermaid-svg-UKHij6aXHrMnBDqb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UKHij6aXHrMnBDqb .rough-node .label,#mermaid-svg-UKHij6aXHrMnBDqb .node .label,#mermaid-svg-UKHij6aXHrMnBDqb .image-shape .label,#mermaid-svg-UKHij6aXHrMnBDqb .icon-shape .label{text-align:center;}#mermaid-svg-UKHij6aXHrMnBDqb .node.clickable{cursor:pointer;}#mermaid-svg-UKHij6aXHrMnBDqb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UKHij6aXHrMnBDqb .arrowheadPath{fill:#333333;}#mermaid-svg-UKHij6aXHrMnBDqb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UKHij6aXHrMnBDqb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UKHij6aXHrMnBDqb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UKHij6aXHrMnBDqb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UKHij6aXHrMnBDqb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UKHij6aXHrMnBDqb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UKHij6aXHrMnBDqb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UKHij6aXHrMnBDqb .cluster text{fill:#333;}#mermaid-svg-UKHij6aXHrMnBDqb .cluster span{color:#333;}#mermaid-svg-UKHij6aXHrMnBDqb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UKHij6aXHrMnBDqb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UKHij6aXHrMnBDqb rect.text{fill:none;stroke-width:0;}#mermaid-svg-UKHij6aXHrMnBDqb .icon-shape,#mermaid-svg-UKHij6aXHrMnBDqb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UKHij6aXHrMnBDqb .icon-shape p,#mermaid-svg-UKHij6aXHrMnBDqb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UKHij6aXHrMnBDqb .icon-shape .label rect,#mermaid-svg-UKHij6aXHrMnBDqb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UKHij6aXHrMnBDqb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UKHij6aXHrMnBDqb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UKHij6aXHrMnBDqb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端
新主从集群
主从同步
读写命令
读写命令
读写命令
读写命令
原 Slave 1
新 Master
Slave 2
Client 1
Client 2
Client 3
Client 4
让应用连接新主节点
阶段6:原主节点恢复,降级为从节点
#mermaid-svg-PLgzadIMm0Zw2R1o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PLgzadIMm0Zw2R1o .error-icon{fill:#552222;}#mermaid-svg-PLgzadIMm0Zw2R1o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PLgzadIMm0Zw2R1o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PLgzadIMm0Zw2R1o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PLgzadIMm0Zw2R1o .marker.cross{stroke:#333333;}#mermaid-svg-PLgzadIMm0Zw2R1o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PLgzadIMm0Zw2R1o p{margin:0;}#mermaid-svg-PLgzadIMm0Zw2R1o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o .cluster-label text{fill:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o .cluster-label span{color:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o .cluster-label span p{background-color:transparent;}#mermaid-svg-PLgzadIMm0Zw2R1o .label text,#mermaid-svg-PLgzadIMm0Zw2R1o span{fill:#333;color:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o .node rect,#mermaid-svg-PLgzadIMm0Zw2R1o .node circle,#mermaid-svg-PLgzadIMm0Zw2R1o .node ellipse,#mermaid-svg-PLgzadIMm0Zw2R1o .node polygon,#mermaid-svg-PLgzadIMm0Zw2R1o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PLgzadIMm0Zw2R1o .rough-node .label text,#mermaid-svg-PLgzadIMm0Zw2R1o .node .label text,#mermaid-svg-PLgzadIMm0Zw2R1o .image-shape .label,#mermaid-svg-PLgzadIMm0Zw2R1o .icon-shape .label{text-anchor:middle;}#mermaid-svg-PLgzadIMm0Zw2R1o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PLgzadIMm0Zw2R1o .rough-node .label,#mermaid-svg-PLgzadIMm0Zw2R1o .node .label,#mermaid-svg-PLgzadIMm0Zw2R1o .image-shape .label,#mermaid-svg-PLgzadIMm0Zw2R1o .icon-shape .label{text-align:center;}#mermaid-svg-PLgzadIMm0Zw2R1o .node.clickable{cursor:pointer;}#mermaid-svg-PLgzadIMm0Zw2R1o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PLgzadIMm0Zw2R1o .arrowheadPath{fill:#333333;}#mermaid-svg-PLgzadIMm0Zw2R1o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PLgzadIMm0Zw2R1o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PLgzadIMm0Zw2R1o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PLgzadIMm0Zw2R1o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PLgzadIMm0Zw2R1o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PLgzadIMm0Zw2R1o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PLgzadIMm0Zw2R1o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PLgzadIMm0Zw2R1o .cluster text{fill:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o .cluster span{color:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PLgzadIMm0Zw2R1o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PLgzadIMm0Zw2R1o rect.text{fill:none;stroke-width:0;}#mermaid-svg-PLgzadIMm0Zw2R1o .icon-shape,#mermaid-svg-PLgzadIMm0Zw2R1o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PLgzadIMm0Zw2R1o .icon-shape p,#mermaid-svg-PLgzadIMm0Zw2R1o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PLgzadIMm0Zw2R1o .icon-shape .label rect,#mermaid-svg-PLgzadIMm0Zw2R1o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PLgzadIMm0Zw2R1o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PLgzadIMm0Zw2R1o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PLgzadIMm0Zw2R1o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端
恢复后主从集群
主从同步
主从同步
读写命令
读写命令
读写命令
读写命令
原 Slave 1
新 Master
Slave 2
原 Master
新 Slave 3
原主节点恢复后
配置其成为一个从节点
Client 1
Client 2
Client 3
Client 4
- 运维人员通过监控系统,发现 Redis 主节点故障。
- 运维人员选定一个从节点(此处选择 slave 1),执行
slaveof no one,使其成为新的主节点。 - 运维人员让剩余从节点(此处为 slave2)执行
slaveof {newMasterIp} {newMasterPort},从新主节点开始数据同步。 - 更新应用方连接的主节点信息为新主节点的地址和端口。
- 如果原主节点恢复,执行
slaveof {newMasterIp} {newMasterPort},让其成为新主的一个从节点。
这个流程的主要缺点就是:全靠人工处理,故障恢复慢,而且容易出错。
具体来说:
-
故障发现依赖人工
- 需要运维人员先发现主节点故障,无法自动处理。
-
主从切换依赖人工
- 需要人工选择新的 Master,并执行
slaveof no one。
- 需要人工选择新的 Master,并执行
-
其他从节点需要手动重新配置
- 每个从节点都要重新指向新的 Master,操作繁琐。
-
客户端也要手动修改
- 应用需要更新新的 Master 地址,否则仍然连接旧 Master。
-
容易出现操作错误
- 主节点选择、配置修改、客户端切换等环节都可能出错。
-
恢复时间不可控
- 从发现故障到完成切换,整个过程取决于运维人员的响应速度。
哨兵自动恢复主节点故障
当主节点出现故障时,Redis Sentinel 能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。
Redis Sentinel 是一个分布式架构,包含若干个 Sentinel 节点和 Redis 数据节点 。每个 Sentinel 节点会对数据节点和其余 Sentinel 节点 进行监控,当它发现节点不可达时,会标记节点下线。如果下线的是主节点,它还会和其他 Sentinel 节点协商 ,当大多数 Sentinel 节点对"主节点不可达"达成共识后,会在内部选举出一个领导节点 来完成自动故障转移,同时将变化实时通知给应用方。整个过程完全自动,不需要人工介入。
Redis Sentinel 架构
#mermaid-svg-ByilREO6L1PSYE9o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ByilREO6L1PSYE9o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ByilREO6L1PSYE9o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ByilREO6L1PSYE9o .error-icon{fill:#552222;}#mermaid-svg-ByilREO6L1PSYE9o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ByilREO6L1PSYE9o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ByilREO6L1PSYE9o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ByilREO6L1PSYE9o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ByilREO6L1PSYE9o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ByilREO6L1PSYE9o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ByilREO6L1PSYE9o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ByilREO6L1PSYE9o .marker.cross{stroke:#333333;}#mermaid-svg-ByilREO6L1PSYE9o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ByilREO6L1PSYE9o p{margin:0;}#mermaid-svg-ByilREO6L1PSYE9o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster-label text{fill:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster-label span{color:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster-label span p{background-color:transparent;}#mermaid-svg-ByilREO6L1PSYE9o .label text,#mermaid-svg-ByilREO6L1PSYE9o span{fill:#333;color:#333;}#mermaid-svg-ByilREO6L1PSYE9o .node rect,#mermaid-svg-ByilREO6L1PSYE9o .node circle,#mermaid-svg-ByilREO6L1PSYE9o .node ellipse,#mermaid-svg-ByilREO6L1PSYE9o .node polygon,#mermaid-svg-ByilREO6L1PSYE9o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .rough-node .label text,#mermaid-svg-ByilREO6L1PSYE9o .node .label text,#mermaid-svg-ByilREO6L1PSYE9o .image-shape .label,#mermaid-svg-ByilREO6L1PSYE9o .icon-shape .label{text-anchor:middle;}#mermaid-svg-ByilREO6L1PSYE9o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .rough-node .label,#mermaid-svg-ByilREO6L1PSYE9o .node .label,#mermaid-svg-ByilREO6L1PSYE9o .image-shape .label,#mermaid-svg-ByilREO6L1PSYE9o .icon-shape .label{text-align:center;}#mermaid-svg-ByilREO6L1PSYE9o .node.clickable{cursor:pointer;}#mermaid-svg-ByilREO6L1PSYE9o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ByilREO6L1PSYE9o .arrowheadPath{fill:#333333;}#mermaid-svg-ByilREO6L1PSYE9o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ByilREO6L1PSYE9o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ByilREO6L1PSYE9o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ByilREO6L1PSYE9o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ByilREO6L1PSYE9o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ByilREO6L1PSYE9o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ByilREO6L1PSYE9o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .cluster text{fill:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster span{color:#333;}#mermaid-svg-ByilREO6L1PSYE9o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ByilREO6L1PSYE9o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ByilREO6L1PSYE9o rect.text{fill:none;stroke-width:0;}#mermaid-svg-ByilREO6L1PSYE9o .icon-shape,#mermaid-svg-ByilREO6L1PSYE9o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ByilREO6L1PSYE9o .icon-shape p,#mermaid-svg-ByilREO6L1PSYE9o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ByilREO6L1PSYE9o .icon-shape .label rect,#mermaid-svg-ByilREO6L1PSYE9o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ByilREO6L1PSYE9o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ByilREO6L1PSYE9o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ByilREO6L1PSYE9o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据节点集合
哨兵节点集合
主从同步
主从同步
监控
监控
监控
监控
监控
监控
监控
监控
监控
sentinel 1
sentinel 2
sentinel 3
master
slave
slave
Redis Sentinel 相比主从复制模式,多了若干(建议保持奇数)Sentinel 节点用于监控数据节点,哨兵节点会定期监控所有节点(包含数据节点和其他哨兵节点)。针对主节点故障的情况,故障转移流程大致如下:
- 主节点故障,从节点同步连接中断,主从复制停止。
- 哨兵节点通过定期监控发现主节点故障,与其他哨兵节点协商,达成"主节点故障"的多数共识。这一步主要防止误判:出问题的不是主节点,而是发现故障的哨兵节点本身(常见于哨兵网络被孤立的场景)。
- 哨兵节点之间使用 Raft 算法选举出一个领导角色,由该节点负责后续的故障转移工作。
- 哨兵领导者执行故障转移:从节点中选择一个作为新主节点;让其他从节点同步新主节点;通知应用层切换到新主节点。
故障转移时间线
#mermaid-svg-hCemRdQsSyMZ6LeK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hCemRdQsSyMZ6LeK .error-icon{fill:#552222;}#mermaid-svg-hCemRdQsSyMZ6LeK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hCemRdQsSyMZ6LeK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hCemRdQsSyMZ6LeK .marker.cross{stroke:#333333;}#mermaid-svg-hCemRdQsSyMZ6LeK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hCemRdQsSyMZ6LeK p{margin:0;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge{stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .section--1 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section--1 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section--1 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section--1 text{fill:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth--1{stroke-width:17;}#mermaid-svg-hCemRdQsSyMZ6LeK .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-0 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-0 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-0 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-0 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-0{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-0{stroke-width:14;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-1 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-1 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-1 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-1 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-1{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-1{stroke-width:11;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-2 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-2 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-2 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-2 text{fill:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-2{stroke-width:8;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-3 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-3 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-3 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-3 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-3{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-3{stroke-width:5;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-4 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-4 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-4 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-4 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-4{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-4{stroke-width:2;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-5 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-5 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-5 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-5 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-5{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-5{stroke-width:-1;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-6 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-6 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-6 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-6 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-6{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-6{stroke-width:-4;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-7 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-7 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-7 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-7 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-7{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-7{stroke-width:-7;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-8 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-8 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-8 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-8 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-8{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-8{stroke-width:-10;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-9 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-9 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-9 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-9 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-9{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-9{stroke-width:-13;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-10 rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-10 path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-10 circle,#mermaid-svg-hCemRdQsSyMZ6LeK .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-10 text{fill:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .node-icon-10{font-size:40px;color:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .edge-depth-10{stroke-width:-16;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-hCemRdQsSyMZ6LeK .lineWrapper line{stroke:black;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled circle,#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:lightgray;}#mermaid-svg-hCemRdQsSyMZ6LeK .disabled text{fill:#efefef;}#mermaid-svg-hCemRdQsSyMZ6LeK .section-root rect,#mermaid-svg-hCemRdQsSyMZ6LeK .section-root path,#mermaid-svg-hCemRdQsSyMZ6LeK .section-root circle{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-hCemRdQsSyMZ6LeK .section-root text{fill:#ffffff;}#mermaid-svg-hCemRdQsSyMZ6LeK .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-hCemRdQsSyMZ6LeK .edge{fill:none;}#mermaid-svg-hCemRdQsSyMZ6LeK .eventWrapper{filter:brightness(120%);}#mermaid-svg-hCemRdQsSyMZ6LeK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 主节点宕机 Sentinel首次发现主节点故障 故障确认 多个 Sentinel达成共识 领导选举 选举出 Sentinel领导节点 故障转移 领导节点开始执行故障转移 切换完成 从节点晋升为新的主节点 Redis Sentinel 故障转移时间线
Redis Sentinel 具有以下几个核心功能:
- 监控:Sentinel 节点会定期检测 Redis 数据节点、其余哨兵节点是否可达。
- 故障转移:实现从节点晋升为主节点,并维护后续正确的主从关系。
- 通知:Sentinel 节点会将故障转移的结果通知给应用方。
安装部署 (基于 docker)
准备工作
- 安装 docker 和 docker-compose
Docker-compose 安装命令:
# ubuntu
apt install docker-compose
# centos
yum install docker-compose
-
停止之前的 redis-server
停止 redis-server
service redis-server stop
停止 redis-sentinel(如果已有运行)
service redis-sentinel stop
-
使用 docker 获取 redis 镜像
docker pull redis:5.0.9
编排 redis 主从节点
- 编写 docker-compose.yml
创建 /root/redis/docker-compose.yml:
yaml
version: '3.7'
services:
master:
image: 'redis:5.0.9'
container_name: redis-master
restart: always
command: redis-server --appendonly yes
ports:
- 6379:6379
slave1:
image: 'redis:5.0.9'
container_name: redis-slave1
restart: always
command: redis-server --appendonly yes --slaveof redis-master 6379
ports:
- 6380:6379
slave2:
image: 'redis:5.0.9'
container_name: redis-slave2
restart: always
command: redis-server --appendonly yes --slaveof redis-master 6379
ports:
- 6381:6379
-
启动所有容器
docker-compose up -d
如果启动后发现配置有误,需要重新操作,使用 docker-compose down 停止并删除刚才创建的容器。
-
查看运行日志
docker-compose logs
-
验证
连接主节点:
redis-cli -p 6379
执行 info replication 输出示例:
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.18.0.2,port=6379,state=online,offset=294,lag=0
slave1:ip=172.18.0.3,port=6379,state=online,offset=294,lag=0
master_replid:ba86e6d49696a7c2ee38ec89487764f0f362bb1d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:294
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:294
127.0.0.1:6379>
连接从节点:
redis-cli -p 6380
执行 info replication 输出示例:
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:redis-master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:1456
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:ba86e6d49696a7c2ee38ec89487764f0f362bb1d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1456
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1456
编排 redis-sentinel 节点
也可以把 redis-sentinel 和 redis 主从放在同一个 yml 中编排,主播这里就分成两组主要有两个原因:
- 观察日志更方便
- 确保 Redis 主从节点启动之后再启动 redis-sentinel。
- 编写 docker-compose.yml
创建 /root/redis-sentinel/docker-compose.yml
yaml
version: '3.7'
services:
# Sentinel 节点 1
sentinel1:
image: 'redis:5.0.9' # 使用 Redis 5.0.9 镜像
container_name: redis-sentinel-1 # 容器名称
restart: always # 容器退出后自动重启
command: redis-sentinel /etc/redis/sentinel.conf # 启动 Sentinel,并加载配置文件
volumes:
- ./sentinel1.conf:/etc/redis/sentinel.conf # 将宿主机 Sentinel 配置文件挂载到容器
ports:
- 26379:26379 # 宿主机 26379 → 容器 26379
# Sentinel 节点 2
sentinel2:
image: 'redis:5.0.9'
container_name: redis-sentinel-2
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel2.conf:/etc/redis/sentinel.conf # 使用 Sentinel 2 的配置文件
ports:
- 26380:26379 # 宿主机 26380 → 容器 26379
# Sentinel 节点 3
sentinel3:
image: 'redis:5.0.9'
container_name: redis-sentinel-3
restart: always
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel3.conf:/etc/redis/sentinel.conf # 使用 Sentinel 3 的配置文件
ports:
- 26381:26379 # 宿主机 26381 → 容器 26379
# 使用已经存在的 Docker 网络,让 Sentinel 能与 Redis 主从节点通信
networks:
default:
external:
name: redis-data_default
- 创建配置文件
创建 sentinel1.conf、sentinel2.conf、sentinel3.conf 三份文件,初始内容完全相同,都放到 /root/redis-sentinel/ 目录中。
bind 0.0.0.0 # 监听所有网卡,允许其他容器连接 Sentinel
port 26379 # Sentinel 监听端口
sentinel monitor redis-master redis-master 6379 2
# 监控名为 redis-master 的主节点
# redis-master:主节点地址
# 6379:主节点端口
# 2:至少 2 个 Sentinel 同意主节点下线,才认为主节点客观下线
sentinel down-after-milliseconds redis-master 1000
# 1 秒内无法与主节点正常通信,则认为主节点主观下线
理解 sentinel monitor
格式:
sentinel monitor 主节点名 主节点ip 主节点端口 法定票数
- 主节点名:哨兵内部自定义的主节点标识名称。
- 主节点ip:部署 redis-master 的设备 IP。此处使用 docker,可以直接写容器名,会被自动 DNS 解析为对应容器 IP。
- 主节点端口:主节点 Redis 服务端口。
- 法定票数:判定主节点下线的同意票数阈值。单个哨兵可能因为自身网络问题误判主节点下线,通过多哨兵投票可以避免误判;当认为主节点下线的哨兵数 ≥ 法定票数时,才会真正判定主节点下线。
理解 sentinel down-after-milliseconds
主节点和哨兵之间通过心跳包通信,如果心跳包在指定时间内没有返回,就视为节点出现故障。
-
启动所有容器
docker-compose up -d
如果启动后发现配置有误,需要重新操作,使用 docker-compose down 即可停止并删除刚才创建的容器。
-
查看运行日志
docker-compose logs
启动后可以看到,哨兵节点已经通过主节点,自动发现了对应的从节点。
日志示例:
1:X 27 Aug 2026 16:01:16.205 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:X 27 Aug 2026 16:01:16.205 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=1, just started
1:X 27 Aug 2026 16:01:16.205 # Configuration loaded
1:X 27 Aug 2026 16:01:16.205 * Running mode=sentinel, port=26379.
1:X 27 Aug 2026 16:01:16.207 # Sentinel ID is c8bdb10b8999ffdc6a5d84fede44f287b2557494
1:X 27 Aug 2026 16:01:16.207 # +monitor master redis-master 172.18.0.4 6379 quorum 2
1:X 27 Aug 2026 16:01:16.208 * +slave slave 172.18.0.2:6379 172.18.0.2 6379 @ redis-master 172.18.0.4 6379
1:X 27 Aug 2026 16:01:16.209 * +slave slave 172.18.0.3:6379 172.18.0.3 6379 @ redis-master 172.18.0.4 6379
1:X 27 Aug 2026 16:01:18.255 * +sentinel sentinel 7fcaa173a0685008dd9129973099c286bfe3a09d 172.18.0.7 26379 @ redis-master 172.18.0.4 6379
重新选举
redis-master 宕机之后
手动把 redis-master 停止:
docker stop redis-master
观察哨兵日志
可以看到哨兵先检测到主节点主观下线(sdown),进一步由于主节点故障得票达到法定阈值,master 最终被判定为客观下线(odown)。
- 主观下线 (Subjectively Down, SDown):单个哨兵感知到主节点心跳异常,单方面判定为主观下线。
- 客观下线 (Objectively Down, ODown):多个哨兵达成一致意见,共同确认 master 确实下线。
随后哨兵集群会挑选出一个新的 master,关键日志示例:
+switch-master redis-master 172.22.0.2 6379 172.22.0.4 6379
整个切换过程对业务无感知,Redis 服务仍然可以正常使用。
redis-master 重启之后
手动把 redis-master 重新启动:
docker start redis-master
观察哨兵日志
可以看到重启后的原主节点会被自动降级为从节点:
+convert-to-slave slave 172.22.0.2:6379 172.22.0.2 6379 @ redis-master 172.22.0.4 6379
使用 redis-cli 可以验证节点角色变化:
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:172.22.0.4
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:324475
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:e6ecc285a2892fba157318c77ebe1409f9c2254e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:324475
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:318295
repl_backlog_histlen:6181
- Redis 主节点宕机后,哨兵会自动从从节点中提拔一个节点成为新的主节点。
- 原主节点重启恢复后,会被哨兵纳入监控,但只会作为新主节点的从节点使用。
选举原理
假定当前环境:3 个哨兵节点(sentinel1、sentinel2、sentinel3),1 个主节点(redis-master),2 个从节点(redis-slave1、redis-slave2)。
当主节点出现故障时,会触发完整的重新选举流程。
哨兵整体架构
#mermaid-svg-ByilREO6L1PSYE9o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ByilREO6L1PSYE9o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ByilREO6L1PSYE9o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ByilREO6L1PSYE9o .error-icon{fill:#552222;}#mermaid-svg-ByilREO6L1PSYE9o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ByilREO6L1PSYE9o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ByilREO6L1PSYE9o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ByilREO6L1PSYE9o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ByilREO6L1PSYE9o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ByilREO6L1PSYE9o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ByilREO6L1PSYE9o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ByilREO6L1PSYE9o .marker.cross{stroke:#333333;}#mermaid-svg-ByilREO6L1PSYE9o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ByilREO6L1PSYE9o p{margin:0;}#mermaid-svg-ByilREO6L1PSYE9o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster-label text{fill:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster-label span{color:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster-label span p{background-color:transparent;}#mermaid-svg-ByilREO6L1PSYE9o .label text,#mermaid-svg-ByilREO6L1PSYE9o span{fill:#333;color:#333;}#mermaid-svg-ByilREO6L1PSYE9o .node rect,#mermaid-svg-ByilREO6L1PSYE9o .node circle,#mermaid-svg-ByilREO6L1PSYE9o .node ellipse,#mermaid-svg-ByilREO6L1PSYE9o .node polygon,#mermaid-svg-ByilREO6L1PSYE9o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .rough-node .label text,#mermaid-svg-ByilREO6L1PSYE9o .node .label text,#mermaid-svg-ByilREO6L1PSYE9o .image-shape .label,#mermaid-svg-ByilREO6L1PSYE9o .icon-shape .label{text-anchor:middle;}#mermaid-svg-ByilREO6L1PSYE9o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .rough-node .label,#mermaid-svg-ByilREO6L1PSYE9o .node .label,#mermaid-svg-ByilREO6L1PSYE9o .image-shape .label,#mermaid-svg-ByilREO6L1PSYE9o .icon-shape .label{text-align:center;}#mermaid-svg-ByilREO6L1PSYE9o .node.clickable{cursor:pointer;}#mermaid-svg-ByilREO6L1PSYE9o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ByilREO6L1PSYE9o .arrowheadPath{fill:#333333;}#mermaid-svg-ByilREO6L1PSYE9o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ByilREO6L1PSYE9o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ByilREO6L1PSYE9o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ByilREO6L1PSYE9o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ByilREO6L1PSYE9o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ByilREO6L1PSYE9o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ByilREO6L1PSYE9o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ByilREO6L1PSYE9o .cluster text{fill:#333;}#mermaid-svg-ByilREO6L1PSYE9o .cluster span{color:#333;}#mermaid-svg-ByilREO6L1PSYE9o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ByilREO6L1PSYE9o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ByilREO6L1PSYE9o rect.text{fill:none;stroke-width:0;}#mermaid-svg-ByilREO6L1PSYE9o .icon-shape,#mermaid-svg-ByilREO6L1PSYE9o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ByilREO6L1PSYE9o .icon-shape p,#mermaid-svg-ByilREO6L1PSYE9o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ByilREO6L1PSYE9o .icon-shape .label rect,#mermaid-svg-ByilREO6L1PSYE9o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ByilREO6L1PSYE9o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ByilREO6L1PSYE9o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ByilREO6L1PSYE9o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据节点集合
哨兵节点集合
主从同步
主从同步
监控
监控
监控
监控
监控
监控
监控
监控
监控
sentinel 1
sentinel 2
sentinel 3
master
slave
slave
故障转移完整流程
#mermaid-svg-cVHF6zv4TdJnZAW8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cVHF6zv4TdJnZAW8 .error-icon{fill:#552222;}#mermaid-svg-cVHF6zv4TdJnZAW8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cVHF6zv4TdJnZAW8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .marker.cross{stroke:#333333;}#mermaid-svg-cVHF6zv4TdJnZAW8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cVHF6zv4TdJnZAW8 p{margin:0;}#mermaid-svg-cVHF6zv4TdJnZAW8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .cluster-label text{fill:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .cluster-label span{color:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .cluster-label span p{background-color:transparent;}#mermaid-svg-cVHF6zv4TdJnZAW8 .label text,#mermaid-svg-cVHF6zv4TdJnZAW8 span{fill:#333;color:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .node rect,#mermaid-svg-cVHF6zv4TdJnZAW8 .node circle,#mermaid-svg-cVHF6zv4TdJnZAW8 .node ellipse,#mermaid-svg-cVHF6zv4TdJnZAW8 .node polygon,#mermaid-svg-cVHF6zv4TdJnZAW8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .rough-node .label text,#mermaid-svg-cVHF6zv4TdJnZAW8 .node .label text,#mermaid-svg-cVHF6zv4TdJnZAW8 .image-shape .label,#mermaid-svg-cVHF6zv4TdJnZAW8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cVHF6zv4TdJnZAW8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .rough-node .label,#mermaid-svg-cVHF6zv4TdJnZAW8 .node .label,#mermaid-svg-cVHF6zv4TdJnZAW8 .image-shape .label,#mermaid-svg-cVHF6zv4TdJnZAW8 .icon-shape .label{text-align:center;}#mermaid-svg-cVHF6zv4TdJnZAW8 .node.clickable{cursor:pointer;}#mermaid-svg-cVHF6zv4TdJnZAW8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .arrowheadPath{fill:#333333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cVHF6zv4TdJnZAW8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cVHF6zv4TdJnZAW8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cVHF6zv4TdJnZAW8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cVHF6zv4TdJnZAW8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .cluster text{fill:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 .cluster span{color:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cVHF6zv4TdJnZAW8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cVHF6zv4TdJnZAW8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cVHF6zv4TdJnZAW8 .icon-shape,#mermaid-svg-cVHF6zv4TdJnZAW8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cVHF6zv4TdJnZAW8 .icon-shape p,#mermaid-svg-cVHF6zv4TdJnZAW8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cVHF6zv4TdJnZAW8 .icon-shape .label rect,#mermaid-svg-cVHF6zv4TdJnZAW8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cVHF6zv4TdJnZAW8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cVHF6zv4TdJnZAW8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cVHF6zv4TdJnZAW8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
主节点宕机
单个哨兵检测到心跳中断
判定主观下线 SDown
多个哨兵互相通信、投票
票数 >= 法定票数?
维持现状,继续监控
判定客观下线 ODown
哨兵集群 Raft 选举 leader
leader 按规则挑选新主节点
提升选中从节点为新 master
其余从节点切换同步新 master
通知客户端新主节点地址
故障转移完成
1) 主观下线
当 redis-master 宕机时,它与三个哨兵之间的心跳连接会断开。redis-master 出现严重故障,三个哨兵都会独立将 redis-master 判定为主观下线 (SDown)。
2) 客观下线
判定主观下线后,sentinel1、sentinel2、sentinel3 会互相通信,对主节点故障这件事进行投票。当故障确认票数 >= 配置的法定票数之后,就会触发客观下线。
法定票数在哨兵配置中定义:
sentinel monitor redis-master 172.22.0.4 6379 2
这里配置的 2 就是法定票数。
当多数哨兵确认主节点故障,就意味着 redis-master 故障被正式确认,此时触发客观下线 (ODown)。
3) 选举出哨兵的 leader
接下来需要从哨兵集群中选出一个领导节点(leader),由它全权负责后续的从节点升级、主从关系调整等故障转移工作。选举过程基于 Raft 算法 实现。
可以,这里其实不用把 Raft 的细节讲得这么复杂。你只需要抓住 "选一个 leader 出来负责故障转移"。
Raft 选举过程
假设有 3 个 Sentinel:S1、S2、S3。
- Sentinel 之间互相投票,每个节点只能投一票。
- 获得多数票(至少 2 票)的 Sentinel 成为 Leader。
- Leader 负责执行故障转移,选择一个 Slave 晋升为新的 Master。
- 选举完成。
不是随机选 Leader,而是按照"先收到谁的拉票请求,就优先给谁投票"的规则进行竞争,因此最终结果可能受网络延迟、消息到达顺序影响
4) leader 挑选合适的 slave 成为新 master
leader 按照以下优先级规则挑选新主节点:
- 先排除不合格节点:与主节点断开时间过长的从节点,不参与选举。
- 优先级优先 :比较
slave-priority/replica-priority,数值越小优先级越高 ;0表示永远不会被提升为主节点。 - 复制偏移量优先 :优先级相同时,比较
replication offset,偏移量越大,说明从主节点处理的数据越多,越优先。 - 运行 ID 最后比较 :前两项都相同时,比较
run ID的字典序(lexicographical) ,字典序更小的优先。这主要是为了让选择结果确定,而不是因为"小 ID 本身更优秀"。
当某个 slave 被指定为新 master 之后:
- leader 向该节点发送命令,执行
slave no one,正式升级为主节点; - leader 通知剩余所有从节点,切换同步到这个新的主节点。
注意事项
- 哨兵节点不能只有一个,否则哨兵节点自身故障会影响整个系统的可用性。
- 哨兵节点数量最好为奇数,方便 leader 选举,得票更容易超过半数。
- 哨兵只负责故障转移,数据存储仍然由 Redis 主从节点负责。
- 哨兵 + 主从复制解决了存储高可用问题,但无法解决单机存储容量不足的问题,当数据量超过单机上限时,这种架构就难以胜任。
集群
概念
- 哨兵模式 解决的是 Master 故障后的自动故障转移 ,但数据仍然由单组 Master / Slave 存储,受单机内存限制。
- 当数据量超过单机内存时,需要进行横向扩容。
- Redis Cluster 将数据划分成多个分片(多组 Master / Slave),每组只存储部分数据,所有分片共同组成完整的数据集。
- 例如:1TB 数据 → 3 组分片 → 每组约存储 1/3 数据。
Redis 集群架构
#mermaid-svg-wJ6zNgcVi7hQ3yrr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .error-icon{fill:#552222;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .marker.cross{stroke:#333333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr p{margin:0;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .cluster-label text{fill:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .cluster-label span{color:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .cluster-label span p{background-color:transparent;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .label text,#mermaid-svg-wJ6zNgcVi7hQ3yrr span{fill:#333;color:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .node rect,#mermaid-svg-wJ6zNgcVi7hQ3yrr .node circle,#mermaid-svg-wJ6zNgcVi7hQ3yrr .node ellipse,#mermaid-svg-wJ6zNgcVi7hQ3yrr .node polygon,#mermaid-svg-wJ6zNgcVi7hQ3yrr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .rough-node .label text,#mermaid-svg-wJ6zNgcVi7hQ3yrr .node .label text,#mermaid-svg-wJ6zNgcVi7hQ3yrr .image-shape .label,#mermaid-svg-wJ6zNgcVi7hQ3yrr .icon-shape .label{text-anchor:middle;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .rough-node .label,#mermaid-svg-wJ6zNgcVi7hQ3yrr .node .label,#mermaid-svg-wJ6zNgcVi7hQ3yrr .image-shape .label,#mermaid-svg-wJ6zNgcVi7hQ3yrr .icon-shape .label{text-align:center;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .node.clickable{cursor:pointer;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .arrowheadPath{fill:#333333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wJ6zNgcVi7hQ3yrr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wJ6zNgcVi7hQ3yrr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wJ6zNgcVi7hQ3yrr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .cluster text{fill:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .cluster span{color:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wJ6zNgcVi7hQ3yrr rect.text{fill:none;stroke-width:0;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .icon-shape,#mermaid-svg-wJ6zNgcVi7hQ3yrr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .icon-shape p,#mermaid-svg-wJ6zNgcVi7hQ3yrr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .icon-shape .label rect,#mermaid-svg-wJ6zNgcVi7hQ3yrr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wJ6zNgcVi7hQ3yrr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wJ6zNgcVi7hQ3yrr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wJ6zNgcVi7hQ3yrr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分片 2
分片 1
分片 0
全量数据集
主从同步
主从同步
主从同步
主从同步
主从同步
主从同步
1/3 数据
1/3 数据
1/3 数据
全部业务数据
Master 1
Slave 1-1
Slave 1-2
Master 2
Slave 2-1
Slave 2-2
Master 3
Slave 3-1
Slave 3-2
架构说明:
- Master1 和 Slave11、Slave12 存储相同数据,占总数据的 1/3
- Master2 和 Slave21、Slave22 存储相同数据,占总数据的 1/3
- Master3 和 Slave31、Slave32 存储相同数据,占总数据的 1/3
- 三组分片存储的数据各不相同,共同组成完整数据集
- 每个 Slave 都是对应 Master 的备份,Master 宕机后对应的 Slave 会自动补位成 Master
- 每一组主从结构都称为一个分片(Sharding)
- 全量数据增长时,只需要增加更多分片即可完成扩容
数据分片算法
1) 哈希求余
原理
设有 N 个分片,使用 [0, N-1] 进行编号。对给定的 key 计算 hash 值,再将结果对 N 取余,得到的结果就是该 key 所属的分片编号。
示例 :N = 3,key 为 hello,对 key 计算 md5 哈希值得到 bc4b2a76b9719d91,将结果对 3 取余为 0,那么 hello 这个 key 就存放到 0 号分片。
#mermaid-svg-z4HtxAJ2wr8MYzT7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .error-icon{fill:#552222;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .marker.cross{stroke:#333333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 p{margin:0;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .cluster-label text{fill:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .cluster-label span{color:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .cluster-label span p{background-color:transparent;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .label text,#mermaid-svg-z4HtxAJ2wr8MYzT7 span{fill:#333;color:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .node rect,#mermaid-svg-z4HtxAJ2wr8MYzT7 .node circle,#mermaid-svg-z4HtxAJ2wr8MYzT7 .node ellipse,#mermaid-svg-z4HtxAJ2wr8MYzT7 .node polygon,#mermaid-svg-z4HtxAJ2wr8MYzT7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .rough-node .label text,#mermaid-svg-z4HtxAJ2wr8MYzT7 .node .label text,#mermaid-svg-z4HtxAJ2wr8MYzT7 .image-shape .label,#mermaid-svg-z4HtxAJ2wr8MYzT7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .rough-node .label,#mermaid-svg-z4HtxAJ2wr8MYzT7 .node .label,#mermaid-svg-z4HtxAJ2wr8MYzT7 .image-shape .label,#mermaid-svg-z4HtxAJ2wr8MYzT7 .icon-shape .label{text-align:center;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .node.clickable{cursor:pointer;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .arrowheadPath{fill:#333333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-z4HtxAJ2wr8MYzT7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z4HtxAJ2wr8MYzT7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-z4HtxAJ2wr8MYzT7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .cluster text{fill:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .cluster span{color:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-z4HtxAJ2wr8MYzT7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .icon-shape,#mermaid-svg-z4HtxAJ2wr8MYzT7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .icon-shape p,#mermaid-svg-z4HtxAJ2wr8MYzT7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .icon-shape .label rect,#mermaid-svg-z4HtxAJ2wr8MYzT7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-z4HtxAJ2wr8MYzT7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-z4HtxAJ2wr8MYzT7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-z4HtxAJ2wr8MYzT7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入 key
计算 hash 值
hash 值 % 分片总数 N
得到分片编号
路由到对应分片
优缺点
- 优点:算法简单高效,数据分配均匀。
- 缺点:扩容时 N 发生变化,原有的映射规则几乎全部失效,需要在节点间大量迁移数据,扩容开销非常大。
2) 一致性哈希算法
为了降低扩容时的数据迁移开销,业界提出了一致性哈希算法。
原理
- 构建哈希环 :将
0 ~ 2^32-1的整数空间映射成一个首尾相连的圆环,数据按顺时针方向增长。 - 分片落位:将 N 个分片分别映射到圆环的某个固定位置上。
- key 路由 :对 key 计算 hash 得到值 H,从 H 在圆环上的位置顺时针向下查找,遇到的第一个分片,就是该 key 所属的分片。
相当于 N 个分片把整个圆环分成了 N 个管辖区间,key 的 hash 值落在哪个区间,就归对应区间的分片管理。
#mermaid-svg-3uEn0pZdL1MJi984{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3uEn0pZdL1MJi984 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3uEn0pZdL1MJi984 .error-icon{fill:#552222;}#mermaid-svg-3uEn0pZdL1MJi984 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3uEn0pZdL1MJi984 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3uEn0pZdL1MJi984 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3uEn0pZdL1MJi984 .marker.cross{stroke:#333333;}#mermaid-svg-3uEn0pZdL1MJi984 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3uEn0pZdL1MJi984 p{margin:0;}#mermaid-svg-3uEn0pZdL1MJi984 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3uEn0pZdL1MJi984 .cluster-label text{fill:#333;}#mermaid-svg-3uEn0pZdL1MJi984 .cluster-label span{color:#333;}#mermaid-svg-3uEn0pZdL1MJi984 .cluster-label span p{background-color:transparent;}#mermaid-svg-3uEn0pZdL1MJi984 .label text,#mermaid-svg-3uEn0pZdL1MJi984 span{fill:#333;color:#333;}#mermaid-svg-3uEn0pZdL1MJi984 .node rect,#mermaid-svg-3uEn0pZdL1MJi984 .node circle,#mermaid-svg-3uEn0pZdL1MJi984 .node ellipse,#mermaid-svg-3uEn0pZdL1MJi984 .node polygon,#mermaid-svg-3uEn0pZdL1MJi984 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3uEn0pZdL1MJi984 .rough-node .label text,#mermaid-svg-3uEn0pZdL1MJi984 .node .label text,#mermaid-svg-3uEn0pZdL1MJi984 .image-shape .label,#mermaid-svg-3uEn0pZdL1MJi984 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3uEn0pZdL1MJi984 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3uEn0pZdL1MJi984 .rough-node .label,#mermaid-svg-3uEn0pZdL1MJi984 .node .label,#mermaid-svg-3uEn0pZdL1MJi984 .image-shape .label,#mermaid-svg-3uEn0pZdL1MJi984 .icon-shape .label{text-align:center;}#mermaid-svg-3uEn0pZdL1MJi984 .node.clickable{cursor:pointer;}#mermaid-svg-3uEn0pZdL1MJi984 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3uEn0pZdL1MJi984 .arrowheadPath{fill:#333333;}#mermaid-svg-3uEn0pZdL1MJi984 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3uEn0pZdL1MJi984 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3uEn0pZdL1MJi984 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3uEn0pZdL1MJi984 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3uEn0pZdL1MJi984 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3uEn0pZdL1MJi984 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3uEn0pZdL1MJi984 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3uEn0pZdL1MJi984 .cluster text{fill:#333;}#mermaid-svg-3uEn0pZdL1MJi984 .cluster span{color:#333;}#mermaid-svg-3uEn0pZdL1MJi984 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3uEn0pZdL1MJi984 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3uEn0pZdL1MJi984 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3uEn0pZdL1MJi984 .icon-shape,#mermaid-svg-3uEn0pZdL1MJi984 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3uEn0pZdL1MJi984 .icon-shape p,#mermaid-svg-3uEn0pZdL1MJi984 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3uEn0pZdL1MJi984 .icon-shape .label rect,#mermaid-svg-3uEn0pZdL1MJi984 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3uEn0pZdL1MJi984 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3uEn0pZdL1MJi984 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3uEn0pZdL1MJi984 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 一致性哈希环(3个分片)
顺时针管辖
顺时针管辖
顺时针管辖
顺时针查找
顺时针查找
顺时针查找
0号分片
1号分片
2号分片
key1
key2
key3
扩容特性
如果新增一个分片,只需要把前一个分片的部分区间数据迁移到新分片上,其余分片的管辖区间完全不受影响。
比如 3 分片扩容为 4 分片,只需要把 0 号分片的部分数据迁移给 3 号分片,1 号、2 号分片的数据完全不用动。
#mermaid-svg-dT2fMa5vPmbQ7iAj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dT2fMa5vPmbQ7iAj .error-icon{fill:#552222;}#mermaid-svg-dT2fMa5vPmbQ7iAj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dT2fMa5vPmbQ7iAj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .marker.cross{stroke:#333333;}#mermaid-svg-dT2fMa5vPmbQ7iAj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dT2fMa5vPmbQ7iAj p{margin:0;}#mermaid-svg-dT2fMa5vPmbQ7iAj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .cluster-label text{fill:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .cluster-label span{color:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .cluster-label span p{background-color:transparent;}#mermaid-svg-dT2fMa5vPmbQ7iAj .label text,#mermaid-svg-dT2fMa5vPmbQ7iAj span{fill:#333;color:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .node rect,#mermaid-svg-dT2fMa5vPmbQ7iAj .node circle,#mermaid-svg-dT2fMa5vPmbQ7iAj .node ellipse,#mermaid-svg-dT2fMa5vPmbQ7iAj .node polygon,#mermaid-svg-dT2fMa5vPmbQ7iAj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .rough-node .label text,#mermaid-svg-dT2fMa5vPmbQ7iAj .node .label text,#mermaid-svg-dT2fMa5vPmbQ7iAj .image-shape .label,#mermaid-svg-dT2fMa5vPmbQ7iAj .icon-shape .label{text-anchor:middle;}#mermaid-svg-dT2fMa5vPmbQ7iAj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .rough-node .label,#mermaid-svg-dT2fMa5vPmbQ7iAj .node .label,#mermaid-svg-dT2fMa5vPmbQ7iAj .image-shape .label,#mermaid-svg-dT2fMa5vPmbQ7iAj .icon-shape .label{text-align:center;}#mermaid-svg-dT2fMa5vPmbQ7iAj .node.clickable{cursor:pointer;}#mermaid-svg-dT2fMa5vPmbQ7iAj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .arrowheadPath{fill:#333333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dT2fMa5vPmbQ7iAj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dT2fMa5vPmbQ7iAj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dT2fMa5vPmbQ7iAj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dT2fMa5vPmbQ7iAj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .cluster text{fill:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj .cluster span{color:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dT2fMa5vPmbQ7iAj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dT2fMa5vPmbQ7iAj rect.text{fill:none;stroke-width:0;}#mermaid-svg-dT2fMa5vPmbQ7iAj .icon-shape,#mermaid-svg-dT2fMa5vPmbQ7iAj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dT2fMa5vPmbQ7iAj .icon-shape p,#mermaid-svg-dT2fMa5vPmbQ7iAj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dT2fMa5vPmbQ7iAj .icon-shape .label rect,#mermaid-svg-dT2fMa5vPmbQ7iAj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dT2fMa5vPmbQ7iAj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dT2fMa5vPmbQ7iAj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dT2fMa5vPmbQ7iAj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 扩容为4分片哈希环
部分区间迁移
剩余区间
区间不变
区间不变
顺时针区间
0号分片
3号分片
1号分片
2号分片
优缺点
- 优点:大幅降低了扩容时的数据搬运量,提升了扩容操作的效率。
- 缺点 :节点数量较少时,数据分配容易不均匀,出现数据倾斜。
3) 哈希槽算法(Redis 采用)
为了解决数据分配不均的问题,Redis Cluster 引入了 哈希槽(Hash Slots) 算法。
原理
Redis 集群总共预设了 16384 个哈希槽 ,编号范围 0 ~ 16383。每一个key对应的槽位是固定的,每个 key 通过 CRC16 算法计算出哈希值,再对 16384 取余,得到对应的槽位:
plain
hash_slot = crc16(key) % 16384
集群中的每个主节点负责管理一部分哈希槽,key 落在哪个槽,就由负责该槽的节点处理。每个分片的节点使用位图来记录自己持有哪些槽位。
分配示例:
- 0 号主节点:负责
[0, 5461],共 5462 个槽位 - 1 号主节点:负责
[5462, 10922],共 5461 个槽位 - 2 号主节点:负责
[10923, 16383],共 5461 个槽位
扩容特性
扩容时只需要从原有节点中迁移部分槽位到新节点即可,不需要重新计算所有 key 的映射。比如新增 3 号节点,只需要从每个原有节点各挪一部分槽位给新节点。
plain
Master0 → 一部分槽位
Master1 → 一部分槽位
Master2 → 一部分槽位
↓
Master3
为什么是 16384 个槽位?
- 正常 Redis 集群节点数不会超过 1000 个,16384 个槽位足够均匀分配。
- 16384 个槽位的位图(bitmap)只占 2KB 左右,节点间同步状态时传输开销很小。
- 槽位数量过多会提升同步成本,16k 是性能和容量的均衡值。
搭建(基于 docker)
搭建规划
本次共创建 11 个 Redis 节点容器:
- 前 9 个节点搭建基础集群(3 主 6 从,每个主节点配 2 个从节点)
- 后 2 个节点用于后续演示集群扩容
第一步:创建目录和配置文件
创建 redis-cluster 目录,目录内新建 generate.sh 脚本,批量生成 11 个节点的配置文件。
generate.sh 内容:
bash
# 生成前9个节点配置
for port in $(seq 1 9); \
do \
mkdir -p redis${port}/; \
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.10${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
# 生成后2个扩容节点配置
for port in $(seq 10 11); \
do \
mkdir -p redis${port}/; \
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
这个脚本就是:
- 一次性创建 11 个 Redis 节点的目录和配置文件。
- 前 9 个节点用于搭建初始 Redis Cluster,后 2 个作为后续扩容节点。
- 每个节点都开启 Cluster 模式、AOF 持久化,并配置自己的集群通信 IP 和端口。
- 这里只是在准备节点配置,还没有真正创建集群或分配 Master/Slave。
执行脚本生成配置:
bash
bash generate.sh
生成的目录结构:
redis-cluster/
├── docker-compose.yml
├── generate.sh
├── redis1/
│ └── redis.conf
├── redis2/
│ └── redis.conf
...
└── redis11/
└── redis.conf
配置项说明
cluster-enabled yes:开启 Redis 集群模式cluster-config-file nodes.conf:集群节点信息自动生成的配置文件cluster-node-timeout 5000:节点心跳失联的超时时间,单位毫秒cluster-announce-ip:节点对外宣告的自身 IP,供其他节点访问cluster-announce-port:节点业务数据端口cluster-announce-bus-port:节点集群总线端口,用于节点间管理通信
第二步:编写 docker-compose.yml
创建自定义网段 172.30.0.0/24,配置 11 个节点的容器、端口映射和固定 IP。
yaml
version: '3.7'
networks:
mynet:
ipam:
config:
- subnet: 172.30.0.0/24 # 创建 mynet 的 Docker 局域网
services:
redis1:
# 使用 Redis 5.0.9 镜像
image: 'redis:5.0.9'
# 给容器指定固定名称,方便 docker exec、docker logs 等操作
container_name: redis1
# 容器异常退出或 Docker 重启后,自动重新启动
restart: always
# 挂载配置文件目录
# 宿主机 ./redis1/ → 容器 /etc/redis/
volumes:
- ./redis1/:/etc/redis/
# 端口映射
# 宿主机端口 : 容器端口
ports:
# Redis 客户端访问端口
# 宿主机 6371 → redis1 容器的 6379
- 6371:6379
# Redis Cluster Bus 集群内部通信端口
# 宿主机 16371 → redis1 容器的 16379
- 16371:16379
# 启动 Redis,并指定使用 redis.conf 配置文件
command: redis-server /etc/redis/redis.conf
# 将 redis1 加入 mynet Docker 网络
networks:
mynet:
# 给 redis1 指定固定的 Docker 内网 IP
ipv4_address: 172.30.0.101
redis2:
image: 'redis:5.0.9'
container_name: redis2
restart: always
volumes:
- ./redis2/:/etc/redis/
ports:
- 6372:6379
- 16372:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.102
redis3:
image: 'redis:5.0.9'
container_name: redis3
restart: always
volumes:
- ./redis3/:/etc/redis/
ports:
- 6373:6379
- 16373:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.103
redis4:
image: 'redis:5.0.9'
container_name: redis4
restart: always
volumes:
- ./redis4/:/etc/redis/
ports:
- 6374:6379
- 16374:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.104
redis5:
image: 'redis:5.0.9'
container_name: redis5
restart: always
volumes:
- ./redis5/:/etc/redis/
ports:
- 6375:6379
- 16375:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.105
redis6:
image: 'redis:5.0.9'
container_name: redis6
restart: always
volumes:
- ./redis6/:/etc/redis/
ports:
- 6376:6379
- 16376:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.106
redis7:
image: 'redis:5.0.9'
container_name: redis7
restart: always
volumes:
- ./redis7/:/etc/redis/
ports:
- 6377:6379
- 16377:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.107
redis8:
image: 'redis:5.0.9'
container_name: redis8
restart: always
volumes:
- ./redis8/:/etc/redis/
ports:
- 6378:6379
- 16378:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.108
redis9:
image: 'redis:5.0.9'
container_name: redis9
restart: always
volumes:
- ./redis9/:/etc/redis/
ports:
- 6379:6379
- 16379:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.109
redis10:
image: 'redis:5.0.9'
container_name: redis10
restart: always
volumes:
- ./redis10/:/etc/redis/
ports:
- 6380:6379
- 16380:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.110
redis11:
image: 'redis:5.0.9'
container_name: redis11
restart: always
volumes:
- ./redis11/:/etc/redis/
ports:
- 6381:6379
- 16381:16379
command: redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.111
第三步:启动容器
在 redis-cluster 目录下执行:
bash
docker-compose up -d
第四步:构建集群
使用 redis-cli 执行集群创建命令,将前 9 个节点组建成 3 主 6 从的集群:
bash
# 创建 Redis Cluster
redis-cli --cluster create \
# 加入 Redis Cluster 的节点
172.30.0.101:6379 \
172.30.0.102:6379 \
172.30.0.103:6379 \
172.30.0.104:6379 \
172.30.0.105:6379 \
172.30.0.106:6379 \
172.30.0.107:6379 \
172.30.0.108:6379 \
172.30.0.109:6379 \
# 每个主节点配置 2 个从节点
# 9 个节点最终分配为:
# 3 个 Master + 6 个 Replica
--cluster-replicas 2
参数说明:
--cluster create:创建集群,后面依次列出所有节点地址--cluster-replicas 2:每个主节点配置 2 个从节点
plain
```text
>>> Performing hash slots allocation on 9 nodes...
# 在 9 个 Redis 节点中分配 Redis Cluster 的 16384 个 Hash Slot
Master[0] -> Slots 0 - 5460
# 第 1 个主节点负责 0~5460 号槽位,共 5461 个
Master[1] -> Slots 5461 - 10922
# 第 2 个主节点负责 5461~10922 号槽位,共 5462 个
Master[2] -> Slots 10923 - 16383
# 第 3 个主节点负责 10923~16383 号槽位,共 5461 个
# 三个 Master 一共覆盖:
# 5461 + 5462 + 5461 = 16384 个槽位
Adding replica 172.30.0.105:6379 to 172.30.0.101:6379
# redis5 作为 redis1 的 Replica(从节点)
Adding replica 172.30.0.106:6379 to 172.30.0.101:6379
# redis6 作为 redis1 的 Replica(从节点)
Adding replica 172.30.0.107:6379 to 172.30.0.102:6379
# redis7 作为 redis2 的 Replica(从节点)
Adding replica 172.30.0.108:6379 to 172.30.0.102:6379
# redis8 作为 redis2 的 Replica(从节点)
Adding replica 172.30.0.109:6379 to 172.30.0.103:6379
# redis9 作为 redis3 的 Replica(从节点)
Adding replica 172.30.0.104:6379 to 172.30.0.103:6379
# redis4 作为 redis3 的 Replica(从节点)
M: 15f9572e7e81195a695f5507ca1b49eccf53b659 172.30.0.101:6379
# M = Master,172.30.0.101 是主节点 redis1
slots:[0-5460] (5461 slots) master
# redis1 负责 0~5460 号槽位
M: b4c01a0a885600b87688bf1c74f4b031746e4d6b 172.30.0.102:6379
# M = Master,172.30.0.102 是主节点 redis2
slots:[5461-10922] (5462 slots) master
# redis2 负责 5461~10922 号槽位
M: 7ac99970b4eed7b1e40b704f1127e4309b93098a 172.30.0.103:6379
# M = Master,172.30.0.103 是主节点 redis3
slots:[10923-16383] (5461 slots) master
# redis3 负责 10923~16383 号槽位
S: e4cad376b31d415496773dc84d0a1fe11891c2ad 172.30.0.104:6379
# S = Slave,172.30.0.104 是从节点 redis4
replicates 7ac99970b4eed7b1e40b704f1127e4309b93098a
# redis4 复制的 Master 是 7ac999...,即 redis3
S: eb89624804c1d5185ccaf1c133f8d7b7b54001fb 172.30.0.105:6379
# S = Slave,172.30.0.105 是从节点 redis5
replicates 15f9572e7e81195a695f5507ca1b49eccf53b659
# redis5 复制的 Master 是 15f957...,即 redis1
S: b0b57cafbce8af451c5cd57b798d084af93e6cc9 172.30.0.106:6379
# S = Slave,172.30.0.106 是从节点 redis6
replicates 15f9572e7e81195a695f5507ca1b49eccf53b659
# redis6 复制的 Master 是 15f957...,即 redis1
S: f32a32b2b77d4d86f8d9e173cc1131950c4be1d2 172.30.0.107:6379
# S = Slave,172.30.0.107 是从节点 redis7
replicates b4c01a0a885600b87688bf1c74f4b031746e4d6b
# redis7 复制的 Master 是 b4c01...,即 redis2
S: f715129df8d8808f3130b2ab58e98b524650dd62 172.30.0.108:6379
# S = Slave,172.30.0.108 是从节点 redis8
replicates b4c01a0a885600b87688bf1c74f4b031746e4d6b
# redis8 复制的 Master 是 b4c01...,即 redis2
S: 0c04db5e9ced335a29cbe0887457b2bc90e7ac8c 172.30.0.109:6379
# S = Slave,172.30.0.109 是从节点 redis9
replicates 7ac99970b4eed7b1e40b704f1127e4309b93098a
# redis9 复制的 Master 是 7ac999...,即 redis3
Can I set the above configuration? (type 'yes' to accept): yes
# Redis CLI 把上面的主从关系和 Slot 分配结果展示出来
# 输入 yes 后,正式应用这套 Cluster 配置
>>> Nodes configuration updated
# 各个 Redis 节点的 Cluster 配置已经更新
>>> Assign a different config epoch to each node
# 给节点分配不同的 config epoch(集群配置版本号)
>>> Sending CLUSTER MEET messages to join the cluster
# 向各个 Redis 节点发送 CLUSTER MEET
# 让这些节点互相认识,并加入同一个 Redis Cluster
Waiting for the cluster to join
# 等待所有节点完成加入和节点信息交换
....
# 等待过程中,节点之间正在建立集群关系
>>> Performing Cluster Check (using node 172.30.0.101:6379)
# 以 redis1(172.30.0.101)为入口,检查整个 Cluster
M: 15f9572e7e81195a695f5507ca1b49eccf53b659 172.30.0.101:6379
slots:[0-5460] (5461 slots) master
2 additional replica(s)
# redis1 是 Master
# 负责 0~5460 号槽位
# 并且有 2 个 Replica:redis5、redis6
S: 0c04db5e9ced335a29cbe0887457b2bc90e7ac8c 172.30.0.109:6379
slots: (0 slots) slave
replicates 7ac99970b4eed7b1e40b704f1127e4309b93098a
# redis9 是 Slave
# 从节点本身不负责 Slot,所以显示 0 slots
# 它复制 redis3
S: e4cad376b31d415496773dc84d0a1fe11891c2ad 172.30.0.104:6379
slots: (0 slots) slave
replicates 7ac99970b4eed7b1e40b704f1127e4309b93098a
# redis4 是 Slave
# 它复制 redis3
S: f32a32b2b77d4d86f8d9e173cc1131950c4be1d2 172.30.0.107:6379
slots: (0 slots) slave
replicates b4c01a0a885600b87688bf1c74f4b031746e4d6b
# redis7 是 Slave
# 它复制 redis2
S: eb89624804c1d5185ccaf1c133f8d7b7b54001fb 172.30.0.105:6379
slots: (0 slots) slave
replicates 15f9572e7e81195a695f5507ca1b49eccf53b659
# redis5 是 Slave
# 它复制 redis1
S: f715129df8d8808f3130b2ab58e98b524650dd62 172.30.0.108:6379
slots: (0 slots) slave
replicates b4c01a0a885600b87688bf1c74f4b031746e4d6b
# redis8 是 Slave
# 它复制 redis2
S: b0b57cafbce8af451c5cd57b798d084af93e6cc9 172.30.0.106:6379
slots: (0 slots) slave
replicates 15f9572e7e81195a695f5507ca1b49eccf53b659
# redis6 是 Slave
# 它复制 redis1
M: b4c01a0a885600b87688bf1c74f4b031746e4d6b 172.30.0.102:6379
slots:[5461-10922] (5462 slots) master
2 additional replica(s)
# redis2 是 Master
# 负责 5461~10922 号槽位
# 并且有 2 个 Replica:redis7、redis8
M: 7ac99970b4eed7b1e40b704f1127e4309b93098a 172.30.0.103:6379
slots:[10923-16383] (5461 slots) master
2 additional replica(s)
# redis3 是 Master
# 负责 10923~16383 号槽位
# 并且有 2 个 Replica:redis9、redis4
[OK] All nodes agree about slots configuration.
# 所有节点对 Slot 的分配结果一致
# 都认为:
# redis1 → 0~5460
# redis2 → 5461~10922
# redis3 → 10923~16383
>>> Check for open slots...
# 检查是否存在没有正确分配给 Master 的 Slot
>>> Check slots coverage...
# 检查 16384 个 Slot 是否全部被覆盖
[OK] All 16384 slots covered.
# 16384 个 Slot 全部已经被 3 个 Master 覆盖
# 表示 Cluster 的 Slot 分配完整,集群创建成功
执行后命令行会输出哈希槽分配方案、主从对应关系,输入 yes 确认配置。
当看到输出 [OK] All 16384 slots covered 时,代表集群搭建完成。
客户端连接
连接集群时需要加上 -c 参数,用于开启集群模式,让 redis-cli 遇到 MOVED 重定向时自动跳转到正确的 Redis 节点。
bash
redis-cli -h 172.30.0.101 -p 6379 -c
172.30.0.101:6379> set key 123456
-> Redirected to slot [12539] located at 172.30.0.103:6379
OK
key 计算得到 12539 槽位 ,该槽位由 172.30.0.103:6379 负责,所以 -c 自动将请求重定向过去执行。
对于一个命令涉及到多个key的情况,会出现一些错误。因为 Redis Cluster 要求同一次涉及多个 Key 的操作必须让这些 Key 属于同一个 Slot ,否则会报
CROSSSLOT,无法重定向到一个节点统一执行。
查看集群节点状态:
bash
172.30.0.103:6379> CLUSTER nodes
15f9572e7e81195a695f5507ca1b49eccf53b659 172.30.0.101:6379@16379 master - 0 1788016749000 1 connected 0-5460
e4cad376b31d415496773dc84d0a1fe11891c2ad 172.30.0.104:6379@16379 slave 7ac99970b4eed7b1e40b704f1127e4309b93098a 0 1788016750946 4 connected
f32a32b2b77d4d86f8d9e173cc1131950c4be1d2 172.30.0.107:6379@16379 slave b4c01a0a885600b87688bf1c74f4b031746e4d6b 0 1788016749000 7 connected
b0b57cafbce8af451c5cd57b798d084af93e6cc9 172.30.0.106:6379@16379 slave 15f9572e7e81195a695f5507ca1b49eccf53b659 0 1788016750536 6 connected
7ac99970b4eed7b1e40b704f1127e4309b93098a 172.30.0.103:6379@16379 myself,master - 0 1788016749000 3 connected 10923-16383
f715129df8d8808f3130b2ab58e98b524650dd62 172.30.0.108:6379@16379 slave b4c01a0a885600b87688bf1c74f4b031746e4d6b 0 1788016749512 8 connected
eb89624804c1d5185ccaf1c133f8d7b7b54001fb 172.30.0.105:6379@16379 slave 15f9572e7e81195a695f5507ca1b49eccf53b659 0 1788016750000 5 connected
0c04db5e9ced335a29cbe0887457b2bc90e7ac8c 172.30.0.109:6379@16379 slave 7ac99970b4eed7b1e40b704f1127e4309b93098a 0 1788016749921 9 connected
b4c01a0a885600b87688bf1c74f4b031746e4d6b 172.30.0.102:6379@16379 master - 0 1788016749512 2 connected 5461-10922
主节点宕机与故障转移
演示效果
手动停止一个主节点,观察集群自动故障转移:
- 停止主节点 redis1:
bash
docker stop redis1
- 连接其他节点查看集群状态,会发现 redis1 节点被标记为
fail,它的其中一个从节点会自动晋升为新的主节点,接管所有槽位。

- 重新启动 redis1:
bash
docker start redis1
- 再次查看状态,原主节点重启后会自动降级为从节点,同步新主节点的数据。
也可以手动执行 CLUSTER FAILOVER 进行主从切换,将原主节点重新恢复为主角色。
故障处理流程
1) 故障判定
集群中的所有节点,都会周期性的使用心跳包进行通信。
- 节点A给节点B发送ping包,B就会给A返回一个pong包。ping和pong除了 message type 属性之外,其他部分都是一样的。这里包含了集群的配置信息(该节点的id,该节点从属于哪个分片,是主节点还是从节点,都属于谁,持槽有哪些slots的位置...)。
- 每个节点每秒钟,都会给一些随机的节点发起ping包,而不是全发一遍。这样设定是为了避免在节点很多的时候,心跳包也非常多
- 当节点A给节点B发起ping包,B不能如期回应的时候,此时A就会尝试重置和B的tcp连接,看能否连接成功。如果仍然连接失败,A就会把B设为 PFAIL 状态 (主观下线) 。
- A判定B为 PFALL 之后,会通过redis 内部的Gossip协议,和其他节点进行沟通,向其他节点确认B的状态。
- 此时A发现其他很多节点,也认为B为 PFALL ,并且数目超过总集群个数的一半,那么A就会把B标记成 FAIL (客观下线),并且把这个消息同步给其他节点(其他节点收到之后,也会把B标记成FAIL)。
有的时候会引起整个集群都宕机(称为 fail 状态)。以下三种情况会出现集群宕机:
- 某个分片,所有的主节点和从节点都挂了。
- 某个分片,主节点挂了,但是没有从节点。
- 超过半数的master节点都挂了。
故障判定流程
#mermaid-svg-2mqUP3PxQXk4SQth{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2mqUP3PxQXk4SQth .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2mqUP3PxQXk4SQth .error-icon{fill:#552222;}#mermaid-svg-2mqUP3PxQXk4SQth .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2mqUP3PxQXk4SQth .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2mqUP3PxQXk4SQth .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2mqUP3PxQXk4SQth .marker.cross{stroke:#333333;}#mermaid-svg-2mqUP3PxQXk4SQth svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2mqUP3PxQXk4SQth p{margin:0;}#mermaid-svg-2mqUP3PxQXk4SQth .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2mqUP3PxQXk4SQth .cluster-label text{fill:#333;}#mermaid-svg-2mqUP3PxQXk4SQth .cluster-label span{color:#333;}#mermaid-svg-2mqUP3PxQXk4SQth .cluster-label span p{background-color:transparent;}#mermaid-svg-2mqUP3PxQXk4SQth .label text,#mermaid-svg-2mqUP3PxQXk4SQth span{fill:#333;color:#333;}#mermaid-svg-2mqUP3PxQXk4SQth .node rect,#mermaid-svg-2mqUP3PxQXk4SQth .node circle,#mermaid-svg-2mqUP3PxQXk4SQth .node ellipse,#mermaid-svg-2mqUP3PxQXk4SQth .node polygon,#mermaid-svg-2mqUP3PxQXk4SQth .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2mqUP3PxQXk4SQth .rough-node .label text,#mermaid-svg-2mqUP3PxQXk4SQth .node .label text,#mermaid-svg-2mqUP3PxQXk4SQth .image-shape .label,#mermaid-svg-2mqUP3PxQXk4SQth .icon-shape .label{text-anchor:middle;}#mermaid-svg-2mqUP3PxQXk4SQth .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2mqUP3PxQXk4SQth .rough-node .label,#mermaid-svg-2mqUP3PxQXk4SQth .node .label,#mermaid-svg-2mqUP3PxQXk4SQth .image-shape .label,#mermaid-svg-2mqUP3PxQXk4SQth .icon-shape .label{text-align:center;}#mermaid-svg-2mqUP3PxQXk4SQth .node.clickable{cursor:pointer;}#mermaid-svg-2mqUP3PxQXk4SQth .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2mqUP3PxQXk4SQth .arrowheadPath{fill:#333333;}#mermaid-svg-2mqUP3PxQXk4SQth .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2mqUP3PxQXk4SQth .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2mqUP3PxQXk4SQth .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2mqUP3PxQXk4SQth .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2mqUP3PxQXk4SQth .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2mqUP3PxQXk4SQth .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2mqUP3PxQXk4SQth .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2mqUP3PxQXk4SQth .cluster text{fill:#333;}#mermaid-svg-2mqUP3PxQXk4SQth .cluster span{color:#333;}#mermaid-svg-2mqUP3PxQXk4SQth div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2mqUP3PxQXk4SQth .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2mqUP3PxQXk4SQth rect.text{fill:none;stroke-width:0;}#mermaid-svg-2mqUP3PxQXk4SQth .icon-shape,#mermaid-svg-2mqUP3PxQXk4SQth .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2mqUP3PxQXk4SQth .icon-shape p,#mermaid-svg-2mqUP3PxQXk4SQth .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2mqUP3PxQXk4SQth .icon-shape .label rect,#mermaid-svg-2mqUP3PxQXk4SQth .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2mqUP3PxQXk4SQth .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2mqUP3PxQXk4SQth .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2mqUP3PxQXk4SQth :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
否
是
节点A向节点B发送ping心跳包
节点B正常返回pong?
维持正常通信状态
重置TCP连接重试通信
重试连接成功?
节点A将B标记为PFAIL
主观下线
通过Gossip协议同步状态
询问其他节点对B的判定
认为B下线的主节点
数量超过集群半数?
维持PFAIL主观下线状态
将B标记为FAIL
客观下线
广播FAIL消息给集群全部节点
2) 故障迁移
上述例子中,B故障,并且A把B FAIL的消息告知集群中的其他节点。
- 如果B是从节点,那么不需要进行故障迁移。
- 如果B是主节点,那么就会由B的从节点(比如C和D)触发故障迁移了。
故障迁移把从节点提拔成主节点,继续给整个redis集群提供支持:
- 参选资格:从节点与主节点长时间失联,超过阈值就失去参选资格。
- 等待竞选 :有资格的从节点随机等待,复制偏移量越大,等待越短,越优先参选。
- 拉票 :等待结束后向其他节点发起投票请求,只有其他正常的主节点有投票权。
- 当选主节点 :获得超过半数主节点的票 后,当选并执行
SLAVEOF NO ONE,其他从节点重新复制它。 - 同步信息 :新主节点通知其他节点,更新集群拓扑信息。
Redis Cluster 的故障转移选举采用类似 Raft 的机制,等待时间最短的从节点优先发起选举并更容易当选。
故障迁移流程
#mermaid-svg-RIZVvCBQgJrrdfUS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RIZVvCBQgJrrdfUS .error-icon{fill:#552222;}#mermaid-svg-RIZVvCBQgJrrdfUS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RIZVvCBQgJrrdfUS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RIZVvCBQgJrrdfUS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RIZVvCBQgJrrdfUS .marker.cross{stroke:#333333;}#mermaid-svg-RIZVvCBQgJrrdfUS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RIZVvCBQgJrrdfUS p{margin:0;}#mermaid-svg-RIZVvCBQgJrrdfUS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS .cluster-label text{fill:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS .cluster-label span{color:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS .cluster-label span p{background-color:transparent;}#mermaid-svg-RIZVvCBQgJrrdfUS .label text,#mermaid-svg-RIZVvCBQgJrrdfUS span{fill:#333;color:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS .node rect,#mermaid-svg-RIZVvCBQgJrrdfUS .node circle,#mermaid-svg-RIZVvCBQgJrrdfUS .node ellipse,#mermaid-svg-RIZVvCBQgJrrdfUS .node polygon,#mermaid-svg-RIZVvCBQgJrrdfUS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RIZVvCBQgJrrdfUS .rough-node .label text,#mermaid-svg-RIZVvCBQgJrrdfUS .node .label text,#mermaid-svg-RIZVvCBQgJrrdfUS .image-shape .label,#mermaid-svg-RIZVvCBQgJrrdfUS .icon-shape .label{text-anchor:middle;}#mermaid-svg-RIZVvCBQgJrrdfUS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RIZVvCBQgJrrdfUS .rough-node .label,#mermaid-svg-RIZVvCBQgJrrdfUS .node .label,#mermaid-svg-RIZVvCBQgJrrdfUS .image-shape .label,#mermaid-svg-RIZVvCBQgJrrdfUS .icon-shape .label{text-align:center;}#mermaid-svg-RIZVvCBQgJrrdfUS .node.clickable{cursor:pointer;}#mermaid-svg-RIZVvCBQgJrrdfUS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RIZVvCBQgJrrdfUS .arrowheadPath{fill:#333333;}#mermaid-svg-RIZVvCBQgJrrdfUS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RIZVvCBQgJrrdfUS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RIZVvCBQgJrrdfUS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RIZVvCBQgJrrdfUS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RIZVvCBQgJrrdfUS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RIZVvCBQgJrrdfUS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RIZVvCBQgJrrdfUS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RIZVvCBQgJrrdfUS .cluster text{fill:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS .cluster span{color:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RIZVvCBQgJrrdfUS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RIZVvCBQgJrrdfUS rect.text{fill:none;stroke-width:0;}#mermaid-svg-RIZVvCBQgJrrdfUS .icon-shape,#mermaid-svg-RIZVvCBQgJrrdfUS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RIZVvCBQgJrrdfUS .icon-shape p,#mermaid-svg-RIZVvCBQgJrrdfUS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RIZVvCBQgJrrdfUS .icon-shape .label rect,#mermaid-svg-RIZVvCBQgJrrdfUS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RIZVvCBQgJrrdfUS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RIZVvCBQgJrrdfUS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RIZVvCBQgJrrdfUS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 从节点
主节点
否
是
否
是
主节点B被标记为FAIL故障
故障节点角色?
无需执行故障迁移
从节点检查参选资格
校验与主节点失联时长
具备参选资格?
放弃本轮竞选
进入随机休眠倒计时
休眠结束,向所有主节点发起拉票
得票数超过主节点总数的一半?
等待下一轮选举周期
晋升为新主节点
执行 slaveof no one
通知其他从节点指向新主节点
同步新集群拓扑给所有节点
集群扩容
集群扩容整体流程
#mermaid-svg-3AQ8gaHszgWcMgkH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3AQ8gaHszgWcMgkH .error-icon{fill:#552222;}#mermaid-svg-3AQ8gaHszgWcMgkH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3AQ8gaHszgWcMgkH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3AQ8gaHszgWcMgkH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3AQ8gaHszgWcMgkH .marker.cross{stroke:#333333;}#mermaid-svg-3AQ8gaHszgWcMgkH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3AQ8gaHszgWcMgkH p{margin:0;}#mermaid-svg-3AQ8gaHszgWcMgkH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH .cluster-label text{fill:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH .cluster-label span{color:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH .cluster-label span p{background-color:transparent;}#mermaid-svg-3AQ8gaHszgWcMgkH .label text,#mermaid-svg-3AQ8gaHszgWcMgkH span{fill:#333;color:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH .node rect,#mermaid-svg-3AQ8gaHszgWcMgkH .node circle,#mermaid-svg-3AQ8gaHszgWcMgkH .node ellipse,#mermaid-svg-3AQ8gaHszgWcMgkH .node polygon,#mermaid-svg-3AQ8gaHszgWcMgkH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3AQ8gaHszgWcMgkH .rough-node .label text,#mermaid-svg-3AQ8gaHszgWcMgkH .node .label text,#mermaid-svg-3AQ8gaHszgWcMgkH .image-shape .label,#mermaid-svg-3AQ8gaHszgWcMgkH .icon-shape .label{text-anchor:middle;}#mermaid-svg-3AQ8gaHszgWcMgkH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3AQ8gaHszgWcMgkH .rough-node .label,#mermaid-svg-3AQ8gaHszgWcMgkH .node .label,#mermaid-svg-3AQ8gaHszgWcMgkH .image-shape .label,#mermaid-svg-3AQ8gaHszgWcMgkH .icon-shape .label{text-align:center;}#mermaid-svg-3AQ8gaHszgWcMgkH .node.clickable{cursor:pointer;}#mermaid-svg-3AQ8gaHszgWcMgkH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3AQ8gaHszgWcMgkH .arrowheadPath{fill:#333333;}#mermaid-svg-3AQ8gaHszgWcMgkH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3AQ8gaHszgWcMgkH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3AQ8gaHszgWcMgkH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3AQ8gaHszgWcMgkH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3AQ8gaHszgWcMgkH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3AQ8gaHszgWcMgkH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3AQ8gaHszgWcMgkH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3AQ8gaHszgWcMgkH .cluster text{fill:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH .cluster span{color:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3AQ8gaHszgWcMgkH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3AQ8gaHszgWcMgkH rect.text{fill:none;stroke-width:0;}#mermaid-svg-3AQ8gaHszgWcMgkH .icon-shape,#mermaid-svg-3AQ8gaHszgWcMgkH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3AQ8gaHszgWcMgkH .icon-shape p,#mermaid-svg-3AQ8gaHszgWcMgkH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3AQ8gaHszgWcMgkH .icon-shape .label rect,#mermaid-svg-3AQ8gaHszgWcMgkH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3AQ8gaHszgWcMgkH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3AQ8gaHszgWcMgkH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3AQ8gaHszgWcMgkH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动扩容流程
添加新主节点进入集群
cluster add-node
新节点成为无slot的空主节点
执行 reshard 重新分片
迁移slot到新主节点
slot与对应数据迁移完成
为新主节点挂载从节点
add-node --cluster-slave
集群扩容完成
第一步: 把新的主节点加入到集群
上面已经把redis1~redis9重新构成了集群,接下来把redis10和redis11也加入集群。这里把redis10作为主机,redis11作为从机。
bash
drw@192 ~ % docker exec redis2 redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379
>>> Adding node 172.30.0.110:6379 to cluster 172.30.0.101:6379
>>> Performing Cluster Check (using node 172.30.0.101:6379)
S: 15f9572e7e81195a695f5507ca1b49eccf53b659 172.30.0.101:6379
slots: (0 slots) slave
replicates eb89624804c1d5185ccaf1c133f8d7b7b54001fb
M: eb89624804c1d5185ccaf1c133f8d7b7b54001fb 172.30.0.105:6379
slots:[0-5460] (5461 slots) master
2 additional replica(s)
S: 0c04db5e9ced335a29cbe0887457b2bc90e7ac8c 172.30.0.109:6379
slots: (0 slots) slave
replicates 7ac99970b4eed7b1e40b704f1127e4309b93098a
M: b4c01a0a885600b87688bf1c74f4b031746e4d6b 172.30.0.102:6379
slots:[5461-10922] (5462 slots) master
2 additional replica(s)
M: 7ac99970b4eed7b1e40b704f1127e4309b93098a 172.30.0.103:6379
slots:[10923-16383] (5461 slots) master
2 additional replica(s)
S: e4cad376b31d415496773dc84d0a1fe11891c2ad 172.30.0.104:6379
slots: (0 slots) slave
replicates 7ac99970b4eed7b1e40b704f1127e4309b93098a
S: f715129df8d8808f3130b2ab58e98b524650dd62 172.30.0.108:6379
slots: (0 slots) slave
replicates b4c01a0a885600b87688bf1c74f4b031746e4d6b
S: b0b57cafbce8af451c5cd57b798d084af93e6cc9 172.30.0.106:6379
slots: (0 slots) slave
replicates eb89624804c1d5185ccaf1c133f8d7b7b54001fb
S: f32a32b2b77d4d86f8d9e173cc1131950c4be1d2 172.30.0.107:6379
slots: (0 slots) slave
replicates b4c01a0a885600b87688bf1c74f4b031746e4d6b
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 172.30.0.110:6379 to make it join the cluster.
[OK] New node added correctly.
172.30.0.110 加入了 以 172.30.0.101:6379 作为集群入口节点
第二步: 重新分配 slots
对 Redis Cluster 重新分配 Hash Slot,把原来 Master 的一部分 Slot 和数据迁移给新节点。
bash
redis-cli --cluster reshard 172.30.0.101:6379
reshard:执行重新分片172.30.0.101:6379:作为操作入口,可以是集群中的任意节点
执行之后,会进入交互式操作,redis会提示用户输入以下内容:
- How many slots do you want to move (from 1 to 16384)? 填写你想从原有 Master 迁移给新 Master 的 Slot 数量。
- What is the receiving node ID? 接收 Slot 的节点 ID
- Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.选择所有的master都拿过来点或从指定的master复制。
Redis Cluster 在 Reshard 过程中存在短暂的数据迁移不一致和访问重定向问题 :正在迁移的 Slot 访问可能遇到
MOVED/ASK等重定向,而未迁移的 Slot 不受影响;迁移完成后恢复正常。睡哦人话就是用户访问某个key时,如果它所在的 Slot 正在迁移 ,请求可能暂时被重定向或访问异常;如果 Slot 没有迁移,访问正常。
第三步: 给新的主节点添加从节点
还需要给新添加到集群的主节点搭配相应的,把 172.30.0.111 加入集群,并指定它作为 172.30.0.110 的从节点。
bash
redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 \
--cluster-slave \
--cluster-master-id [172.30.0.110 的 nodeId]
参数含义:
add-node:向集群添加一个新节点172.30.0.111:6379:要加入的新节点172.30.0.101:6379:集群中任意一个已有节点,作为联系集群的入口--cluster-slave:指定新节点作为 Replica(从节点)--cluster-master-id:指定这个 Replica 跟随哪个 Master[172.30.0.110 的 nodeId]:填写新 Master172.30.0.110的 Node ID
集群缩容
集群缩容整体流程
#mermaid-svg-gzeacxnAOS0vrYXG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gzeacxnAOS0vrYXG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gzeacxnAOS0vrYXG .error-icon{fill:#552222;}#mermaid-svg-gzeacxnAOS0vrYXG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gzeacxnAOS0vrYXG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gzeacxnAOS0vrYXG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gzeacxnAOS0vrYXG .marker.cross{stroke:#333333;}#mermaid-svg-gzeacxnAOS0vrYXG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gzeacxnAOS0vrYXG p{margin:0;}#mermaid-svg-gzeacxnAOS0vrYXG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gzeacxnAOS0vrYXG .cluster-label text{fill:#333;}#mermaid-svg-gzeacxnAOS0vrYXG .cluster-label span{color:#333;}#mermaid-svg-gzeacxnAOS0vrYXG .cluster-label span p{background-color:transparent;}#mermaid-svg-gzeacxnAOS0vrYXG .label text,#mermaid-svg-gzeacxnAOS0vrYXG span{fill:#333;color:#333;}#mermaid-svg-gzeacxnAOS0vrYXG .node rect,#mermaid-svg-gzeacxnAOS0vrYXG .node circle,#mermaid-svg-gzeacxnAOS0vrYXG .node ellipse,#mermaid-svg-gzeacxnAOS0vrYXG .node polygon,#mermaid-svg-gzeacxnAOS0vrYXG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gzeacxnAOS0vrYXG .rough-node .label text,#mermaid-svg-gzeacxnAOS0vrYXG .node .label text,#mermaid-svg-gzeacxnAOS0vrYXG .image-shape .label,#mermaid-svg-gzeacxnAOS0vrYXG .icon-shape .label{text-anchor:middle;}#mermaid-svg-gzeacxnAOS0vrYXG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gzeacxnAOS0vrYXG .rough-node .label,#mermaid-svg-gzeacxnAOS0vrYXG .node .label,#mermaid-svg-gzeacxnAOS0vrYXG .image-shape .label,#mermaid-svg-gzeacxnAOS0vrYXG .icon-shape .label{text-align:center;}#mermaid-svg-gzeacxnAOS0vrYXG .node.clickable{cursor:pointer;}#mermaid-svg-gzeacxnAOS0vrYXG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gzeacxnAOS0vrYXG .arrowheadPath{fill:#333333;}#mermaid-svg-gzeacxnAOS0vrYXG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gzeacxnAOS0vrYXG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gzeacxnAOS0vrYXG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gzeacxnAOS0vrYXG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gzeacxnAOS0vrYXG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gzeacxnAOS0vrYXG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gzeacxnAOS0vrYXG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gzeacxnAOS0vrYXG .cluster text{fill:#333;}#mermaid-svg-gzeacxnAOS0vrYXG .cluster span{color:#333;}#mermaid-svg-gzeacxnAOS0vrYXG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gzeacxnAOS0vrYXG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gzeacxnAOS0vrYXG rect.text{fill:none;stroke-width:0;}#mermaid-svg-gzeacxnAOS0vrYXG .icon-shape,#mermaid-svg-gzeacxnAOS0vrYXG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gzeacxnAOS0vrYXG .icon-shape p,#mermaid-svg-gzeacxnAOS0vrYXG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gzeacxnAOS0vrYXG .icon-shape .label rect,#mermaid-svg-gzeacxnAOS0vrYXG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gzeacxnAOS0vrYXG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gzeacxnAOS0vrYXG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gzeacxnAOS0vrYXG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动缩容流程
删除待下线主节点对应的从节点
cluster del-node
分批执行 reshard
迁移待下线节点的所有slot
待下线节点不再持有任何slot
删除待下线的主节点
cluster del-node
集群缩容完成
具体的操作步骤就省略了,因为使用场景较少,嗯就这样。