Redis分布式模式深度解析:哨兵模式与集群模式全对比
-
- 引言
- 1. 分布式模式概述
-
- 1.1 为什么要分布式?
- 1.2 两种模式的核心区别
- 2. 哨兵模式(Sentinel)详解
-
- 2.1 架构图
- 2.2 哨兵模式的核心功能
- 2.3 故障转移流程
- 2.4 哨兵配置示例
- 2.5 客户端连接示例
- 2.6 哨兵模式的优缺点
- 3. 集群模式(Cluster)详解
-
- 3.1 架构图
- 3.2 数据分片原理
- 3.3 请求路由流程
- 3.4 集群配置示例
- 3.5 集群创建命令
- 3.6 集群节点信息
- 3.7 客户端连接示例
- 3.8 Spring Boot集成
- 4. 两种模式对比
-
- 4.1 特性对比表
- 4.2 选型决策树
- 4.3 性能对比测试
- 5. 常见问题与解决方案
-
- 5.1 集群模式的限制
- 5.2 监控告警
- 6. 选型建议
-
- 6.1 场景一:中小规模电商系统
- 6.2 场景二:大型社交平台
- 7. 总结
-
- 7.1 两种模式的本质区别
- 7.2 核心观点
- 7.3 最终建议
|
🌺The Begin🌺点点关注,收藏不迷路🌺 |
引言
随着业务规模的扩大,单机Redis已经无法满足高可用和高并发的需求。Redis提供了两种分布式模式:哨兵模式和集群模式。虽然它们都能实现高可用,但设计目标和适用场景截然不同。本文将深入剖析这两种模式的架构原理、核心区别以及选型建议。
1. 分布式模式概述
1.1 为什么要分布式?
#mermaid-svg-WTMXhhEBzGYlT9UZ{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-WTMXhhEBzGYlT9UZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WTMXhhEBzGYlT9UZ .error-icon{fill:#552222;}#mermaid-svg-WTMXhhEBzGYlT9UZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WTMXhhEBzGYlT9UZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .marker.cross{stroke:#333333;}#mermaid-svg-WTMXhhEBzGYlT9UZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WTMXhhEBzGYlT9UZ p{margin:0;}#mermaid-svg-WTMXhhEBzGYlT9UZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .cluster-label text{fill:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .cluster-label span{color:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .cluster-label span p{background-color:transparent;}#mermaid-svg-WTMXhhEBzGYlT9UZ .label text,#mermaid-svg-WTMXhhEBzGYlT9UZ span{fill:#333;color:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .node rect,#mermaid-svg-WTMXhhEBzGYlT9UZ .node circle,#mermaid-svg-WTMXhhEBzGYlT9UZ .node ellipse,#mermaid-svg-WTMXhhEBzGYlT9UZ .node polygon,#mermaid-svg-WTMXhhEBzGYlT9UZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .rough-node .label text,#mermaid-svg-WTMXhhEBzGYlT9UZ .node .label text,#mermaid-svg-WTMXhhEBzGYlT9UZ .image-shape .label,#mermaid-svg-WTMXhhEBzGYlT9UZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-WTMXhhEBzGYlT9UZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .rough-node .label,#mermaid-svg-WTMXhhEBzGYlT9UZ .node .label,#mermaid-svg-WTMXhhEBzGYlT9UZ .image-shape .label,#mermaid-svg-WTMXhhEBzGYlT9UZ .icon-shape .label{text-align:center;}#mermaid-svg-WTMXhhEBzGYlT9UZ .node.clickable{cursor:pointer;}#mermaid-svg-WTMXhhEBzGYlT9UZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .arrowheadPath{fill:#333333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WTMXhhEBzGYlT9UZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WTMXhhEBzGYlT9UZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WTMXhhEBzGYlT9UZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WTMXhhEBzGYlT9UZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .cluster text{fill:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ .cluster span{color:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ 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-WTMXhhEBzGYlT9UZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WTMXhhEBzGYlT9UZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-WTMXhhEBzGYlT9UZ .icon-shape,#mermaid-svg-WTMXhhEBzGYlT9UZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WTMXhhEBzGYlT9UZ .icon-shape p,#mermaid-svg-WTMXhhEBzGYlT9UZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WTMXhhEBzGYlT9UZ .icon-shape rect,#mermaid-svg-WTMXhhEBzGYlT9UZ .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WTMXhhEBzGYlT9UZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WTMXhhEBzGYlT9UZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WTMXhhEBzGYlT9UZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
分布式解决方案
哨兵模式
解决单点故障
集群模式
解决性能瓶颈
单机Redis的瓶颈
单点故障
服务不可用
内存有限
无法存储海量数据
CPU有限
无法承载高并发
1.2 两种模式的核心区别
| 核心目标 | 高可用(解决单点故障) | 水平扩展(解决性能瓶颈) |
| 架构特点 | 单主多从 | 多主多从 |
| 数据分片 | 不支持,全量数据 | 支持,16384个槽位 |
| 扩展性 | 读扩展(加从节点) | 读写扩展(加主节点) |
| 故障转移 | 哨兵自动完成 | 集群自动完成 |
| 客户端 | 连接哨兵获取主节点 | 连接任意节点,自动重定向 |
2. 哨兵模式(Sentinel)详解
2.1 架构图
#mermaid-svg-5JGxQbohikIJ0att{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-5JGxQbohikIJ0att .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5JGxQbohikIJ0att .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5JGxQbohikIJ0att .error-icon{fill:#552222;}#mermaid-svg-5JGxQbohikIJ0att .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5JGxQbohikIJ0att .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5JGxQbohikIJ0att .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5JGxQbohikIJ0att .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5JGxQbohikIJ0att .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5JGxQbohikIJ0att .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5JGxQbohikIJ0att .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5JGxQbohikIJ0att .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5JGxQbohikIJ0att .marker.cross{stroke:#333333;}#mermaid-svg-5JGxQbohikIJ0att svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5JGxQbohikIJ0att p{margin:0;}#mermaid-svg-5JGxQbohikIJ0att .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5JGxQbohikIJ0att .cluster-label text{fill:#333;}#mermaid-svg-5JGxQbohikIJ0att .cluster-label span{color:#333;}#mermaid-svg-5JGxQbohikIJ0att .cluster-label span p{background-color:transparent;}#mermaid-svg-5JGxQbohikIJ0att .label text,#mermaid-svg-5JGxQbohikIJ0att span{fill:#333;color:#333;}#mermaid-svg-5JGxQbohikIJ0att .node rect,#mermaid-svg-5JGxQbohikIJ0att .node circle,#mermaid-svg-5JGxQbohikIJ0att .node ellipse,#mermaid-svg-5JGxQbohikIJ0att .node polygon,#mermaid-svg-5JGxQbohikIJ0att .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5JGxQbohikIJ0att .rough-node .label text,#mermaid-svg-5JGxQbohikIJ0att .node .label text,#mermaid-svg-5JGxQbohikIJ0att .image-shape .label,#mermaid-svg-5JGxQbohikIJ0att .icon-shape .label{text-anchor:middle;}#mermaid-svg-5JGxQbohikIJ0att .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5JGxQbohikIJ0att .rough-node .label,#mermaid-svg-5JGxQbohikIJ0att .node .label,#mermaid-svg-5JGxQbohikIJ0att .image-shape .label,#mermaid-svg-5JGxQbohikIJ0att .icon-shape .label{text-align:center;}#mermaid-svg-5JGxQbohikIJ0att .node.clickable{cursor:pointer;}#mermaid-svg-5JGxQbohikIJ0att .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5JGxQbohikIJ0att .arrowheadPath{fill:#333333;}#mermaid-svg-5JGxQbohikIJ0att .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5JGxQbohikIJ0att .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5JGxQbohikIJ0att .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5JGxQbohikIJ0att .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5JGxQbohikIJ0att .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5JGxQbohikIJ0att .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5JGxQbohikIJ0att .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5JGxQbohikIJ0att .cluster text{fill:#333;}#mermaid-svg-5JGxQbohikIJ0att .cluster span{color:#333;}#mermaid-svg-5JGxQbohikIJ0att 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-5JGxQbohikIJ0att .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5JGxQbohikIJ0att rect.text{fill:none;stroke-width:0;}#mermaid-svg-5JGxQbohikIJ0att .icon-shape,#mermaid-svg-5JGxQbohikIJ0att .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5JGxQbohikIJ0att .icon-shape p,#mermaid-svg-5JGxQbohikIJ0att .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5JGxQbohikIJ0att .icon-shape rect,#mermaid-svg-5JGxQbohikIJ0att .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5JGxQbohikIJ0att .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5JGxQbohikIJ0att .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5JGxQbohikIJ0att :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
数据层
哨兵层
客户端层
获取主节点地址
读写分离
读请求
读请求
复制
复制
监控
监控
监控
互相通信
互相通信
应用客户端
Sentinel1
Sentinel2
Sentinel3
主节点
从节点1
从节点2
2.2 哨兵模式的核心功能
| 监控 | 检查主从节点是否正常运行 |
| 通知 | 故障时通知管理员或客户端 |
| 自动故障转移 | 主节点宕机,选举新主节点 |
| 配置中心 | 客户端通过哨兵获取当前主节点地址 |
2.3 故障转移流程
Client
其他从节点
Slave
Master(宕机)
Sentinel3
Sentinel2
Sentinel1
Client
其他从节点
Slave
Master(宕机)
Sentinel3
Sentinel2
Sentinel1
#mermaid-svg-N7KF5OqJZfYCMl9o{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-N7KF5OqJZfYCMl9o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N7KF5OqJZfYCMl9o .error-icon{fill:#552222;}#mermaid-svg-N7KF5OqJZfYCMl9o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N7KF5OqJZfYCMl9o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N7KF5OqJZfYCMl9o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N7KF5OqJZfYCMl9o .marker.cross{stroke:#333333;}#mermaid-svg-N7KF5OqJZfYCMl9o svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N7KF5OqJZfYCMl9o p{margin:0;}#mermaid-svg-N7KF5OqJZfYCMl9o .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-N7KF5OqJZfYCMl9o text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-N7KF5OqJZfYCMl9o .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-N7KF5OqJZfYCMl9o .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-N7KF5OqJZfYCMl9o #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-N7KF5OqJZfYCMl9o .sequenceNumber{fill:white;}#mermaid-svg-N7KF5OqJZfYCMl9o #sequencenumber{fill:#333;}#mermaid-svg-N7KF5OqJZfYCMl9o #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-N7KF5OqJZfYCMl9o .messageText{fill:#333;stroke:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-N7KF5OqJZfYCMl9o .labelText,#mermaid-svg-N7KF5OqJZfYCMl9o .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .loopText,#mermaid-svg-N7KF5OqJZfYCMl9o .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-N7KF5OqJZfYCMl9o .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-N7KF5OqJZfYCMl9o .noteText,#mermaid-svg-N7KF5OqJZfYCMl9o .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-N7KF5OqJZfYCMl9o .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-N7KF5OqJZfYCMl9o .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-N7KF5OqJZfYCMl9o .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-N7KF5OqJZfYCMl9o .actorPopupMenu{position:absolute;}#mermaid-svg-N7KF5OqJZfYCMl9o .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-N7KF5OqJZfYCMl9o .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-N7KF5OqJZfYCMl9o .actor-man circle,#mermaid-svg-N7KF5OqJZfYCMl9o line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-N7KF5OqJZfYCMl9o :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
响应超时
响应超时
达到法定人数(2个哨兵确认)
当选领导者
根据优先级、复制偏移量选择
发送PING
主观下线,请求确认
发送PING
确认主观下线
发起故障转移投票
投票给S1
投票给S1
选举新主节点
执行slaveof no one
成为新主节点
slaveof 新主节点
通知主节点变更
2.4 哨兵配置示例
# sentinel.conf
port 26379
daemonize yes
pidfile /var/run/redis-sentinel.pid
logfile /var/log/redis-sentinel.log
# 监控主节点,2表示需要2个哨兵同意才判定故障
sentinel monitor mymaster 192.168.1.100 6379 2
sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
# 通知脚本
sentinel notification-script mymaster /opt/redis/notify.sh
2.5 客户端连接示例
public class SentinelJedisExample {
public static void main(String[] args) {
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.1.101:26379");
sentinels.add("192.168.1.102:26379");
sentinels.add("192.168.1.103:26379");
// 创建哨兵连接池
JedisSentinelPool pool = new JedisSentinelPool(
"mymaster", // 主节点名称
sentinels, // 哨兵节点列表
"123456" // 密码
);
try (Jedis jedis = pool.getResource()) {
jedis.set("key", "value");
String value = jedis.get("key");
System.out.println(value);
}
pool.close();
}
}
2.6 哨兵模式的优缺点
| 高可用,自动故障转移 | 单主节点,写能力有限 |
| 架构简单,易于部署 | 数据量受单机内存限制 |
| 支持读写分离 | 扩展性差,无法水平扩容 |
| 成熟稳定,广泛使用 | 客户端需要感知哨兵 |
3. 集群模式(Cluster)详解
3.1 架构图
#mermaid-svg-pjn7zuN5UkzmlaLc{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-pjn7zuN5UkzmlaLc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pjn7zuN5UkzmlaLc .error-icon{fill:#552222;}#mermaid-svg-pjn7zuN5UkzmlaLc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pjn7zuN5UkzmlaLc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pjn7zuN5UkzmlaLc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pjn7zuN5UkzmlaLc .marker.cross{stroke:#333333;}#mermaid-svg-pjn7zuN5UkzmlaLc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pjn7zuN5UkzmlaLc p{margin:0;}#mermaid-svg-pjn7zuN5UkzmlaLc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc .cluster-label text{fill:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc .cluster-label span{color:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc .cluster-label span p{background-color:transparent;}#mermaid-svg-pjn7zuN5UkzmlaLc .label text,#mermaid-svg-pjn7zuN5UkzmlaLc span{fill:#333;color:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc .node rect,#mermaid-svg-pjn7zuN5UkzmlaLc .node circle,#mermaid-svg-pjn7zuN5UkzmlaLc .node ellipse,#mermaid-svg-pjn7zuN5UkzmlaLc .node polygon,#mermaid-svg-pjn7zuN5UkzmlaLc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pjn7zuN5UkzmlaLc .rough-node .label text,#mermaid-svg-pjn7zuN5UkzmlaLc .node .label text,#mermaid-svg-pjn7zuN5UkzmlaLc .image-shape .label,#mermaid-svg-pjn7zuN5UkzmlaLc .icon-shape .label{text-anchor:middle;}#mermaid-svg-pjn7zuN5UkzmlaLc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pjn7zuN5UkzmlaLc .rough-node .label,#mermaid-svg-pjn7zuN5UkzmlaLc .node .label,#mermaid-svg-pjn7zuN5UkzmlaLc .image-shape .label,#mermaid-svg-pjn7zuN5UkzmlaLc .icon-shape .label{text-align:center;}#mermaid-svg-pjn7zuN5UkzmlaLc .node.clickable{cursor:pointer;}#mermaid-svg-pjn7zuN5UkzmlaLc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pjn7zuN5UkzmlaLc .arrowheadPath{fill:#333333;}#mermaid-svg-pjn7zuN5UkzmlaLc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pjn7zuN5UkzmlaLc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pjn7zuN5UkzmlaLc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pjn7zuN5UkzmlaLc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pjn7zuN5UkzmlaLc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pjn7zuN5UkzmlaLc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pjn7zuN5UkzmlaLc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pjn7zuN5UkzmlaLc .cluster text{fill:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc .cluster span{color:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc 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-pjn7zuN5UkzmlaLc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pjn7zuN5UkzmlaLc rect.text{fill:none;stroke-width:0;}#mermaid-svg-pjn7zuN5UkzmlaLc .icon-shape,#mermaid-svg-pjn7zuN5UkzmlaLc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pjn7zuN5UkzmlaLc .icon-shape p,#mermaid-svg-pjn7zuN5UkzmlaLc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pjn7zuN5UkzmlaLc .icon-shape rect,#mermaid-svg-pjn7zuN5UkzmlaLc .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pjn7zuN5UkzmlaLc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pjn7zuN5UkzmlaLc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pjn7zuN5UkzmlaLc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Redis集群
主节点3
主节点2
主节点1
请求key
请求key
请求key
复制
复制
复制
复制
复制
复制
Master1槽位0-5460
Slave1-1
Slave1-2
Master2槽位5461-10922
Slave2-1
Slave2-2
Master3槽位10923-16383
Slave3-1
Slave3-2
客户端
3.2 数据分片原理
Redis集群使用一致性哈希槽进行数据分片:
public class ClusterSharding {
// 总槽位数
public static final int HASH_SLOTS = 16384;
/**
* 计算key的槽位
*/
public static int getSlot(String key) {
// 如果key包含{},只计算{}内的部分
int start = key.indexOf('{');
if (start >= 0) {
int end = key.indexOf('}', start);
if (end >= 0 && end != start + 1) {
key = key.substring(start + 1, end);
}
}
// 使用CRC16算法计算哈希值
int crc = CRC16.crc16(key.getBytes());
// 对16384取模得到槽位
return crc % HASH_SLOTS;
}
/**
* 槽位分布示例
*/
public void showSlotDistribution() {
Map<Integer, String> slotMap = new HashMap<>();
// 节点1负责槽位0-5460
for (int i = 0; i <= 5460; i++) {
slotMap.put(i, "node1");
}
// 节点2负责槽位5461-10922
for (int i = 5461; i <= 10922; i++) {
slotMap.put(i, "node2");
}
// 节点3负责槽位10923-16383
for (int i = 10923; i < 16384; i++) {
slotMap.put(i, "node3");
}
}
}
3.3 请求路由流程
Redis节点2
Redis节点1
Client
Redis节点2
Redis节点1
Client
#mermaid-svg-Dj0ktgjNQtmelrJN{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-Dj0ktgjNQtmelrJN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Dj0ktgjNQtmelrJN .error-icon{fill:#552222;}#mermaid-svg-Dj0ktgjNQtmelrJN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Dj0ktgjNQtmelrJN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Dj0ktgjNQtmelrJN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Dj0ktgjNQtmelrJN .marker.cross{stroke:#333333;}#mermaid-svg-Dj0ktgjNQtmelrJN svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Dj0ktgjNQtmelrJN p{margin:0;}#mermaid-svg-Dj0ktgjNQtmelrJN .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Dj0ktgjNQtmelrJN text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Dj0ktgjNQtmelrJN .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Dj0ktgjNQtmelrJN .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Dj0ktgjNQtmelrJN #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Dj0ktgjNQtmelrJN .sequenceNumber{fill:white;}#mermaid-svg-Dj0ktgjNQtmelrJN #sequencenumber{fill:#333;}#mermaid-svg-Dj0ktgjNQtmelrJN #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Dj0ktgjNQtmelrJN .messageText{fill:#333;stroke:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Dj0ktgjNQtmelrJN .labelText,#mermaid-svg-Dj0ktgjNQtmelrJN .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .loopText,#mermaid-svg-Dj0ktgjNQtmelrJN .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Dj0ktgjNQtmelrJN .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Dj0ktgjNQtmelrJN .noteText,#mermaid-svg-Dj0ktgjNQtmelrJN .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Dj0ktgjNQtmelrJN .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Dj0ktgjNQtmelrJN .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Dj0ktgjNQtmelrJN .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Dj0ktgjNQtmelrJN .actorPopupMenu{position:absolute;}#mermaid-svg-Dj0ktgjNQtmelrJN .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Dj0ktgjNQtmelrJN .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Dj0ktgjNQtmelrJN .actor-man circle,#mermaid-svg-Dj0ktgjNQtmelrJN line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Dj0ktgjNQtmelrJN :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
alt
[客户端支持集群]
[客户端不支持集群]
智能客户端缓存槽位映射关系
SET key value
计算key的槽位: 12345
查找槽位12345所属节点: Node2
MOVED 12345 192.168.1.102:6379
SET key value
OK
MOVED重定向
需要自行处理重定向
3.4 集群配置示例
# redis-cluster.conf
port 7000
daemonize yes
pidfile /var/run/redis_7000.pid
logfile /var/log/redis_7000.log
dir /opt/redis/data/7000
# 开启集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7000.conf
# 节点超时时间
cluster-node-timeout 15000
# 集群节点IP
cluster-announce-ip 192.168.1.101
cluster-announce-port 7000
cluster-announce-bus-port 17000
# 开启AOF
appendonly yes
appendfilename "appendonly-7000.aof"
# 密码设置
requirepass 123456
masterauth 123456
3.5 集群创建命令
# 创建6个节点(3主3从)
redis-cli –cluster create \\
192.168.1.101:7000 \\
192.168.1.102:7001 \\
192.168.1.103:7002 \\
192.168.1.104:7003 \\
192.168.1.105:7004 \\
192.168.1.106:7005 \\
–cluster-replicas 1 \\
-a 123456
# 查看集群信息
redis-cli -h 192.168.1.101 -p 7000 -a 123456 cluster nodes
# 检查集群状态
redis-cli –cluster check 192.168.1.101:7000 -a 123456
3.6 集群节点信息
# cluster nodes 输出示例
07c37dfeb235213a872421d087e0aec2a0839e75 192.168.1.101:7000@17000 master – 0 1623456789 1 connected 0-5460
67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 192.168.1.102:7001@17001 master – 0 1623456790 2 connected 5461-10922
6f70f10ec3fb15a0359c6c7c7a6066b4cb8c3f9e 192.168.1.103:7002@17002 master – 0 1623456791 3 connected 10923-16383
8e9b4e6a7b3d8c5f1a2b4c6d7e8f9a0b1c2d3e4f 192.168.1.104:7003@17003 slave 07c37dfeb235213a872421d087e0aec2a0839e75 0 1623456792 1 connected
9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b 192.168.1.105:7004@17004 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1623456793 2 connected
1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b 192.168.1.106:7005@17005 slave 6f70f10ec3fb15a0359c6c7c7a6066b4cb8c3f9e 0 1623456794 3 connected
3.7 客户端连接示例
public class ClusterJedisExample {
public static void main(String[] args) {
// 创建集群连接
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.1.101", 7000));
nodes.add(new HostAndPort("192.168.1.102", 7001));
nodes.add(new HostAndPort("192.168.1.103", 7002));
// 创建Jedis集群连接池
JedisCluster jedisCluster = new JedisCluster(
nodes,
5000, // 连接超时
5000, // 读取超时
3, // 最大重试次数
"123456", // 密码
new GenericObjectPoolConfig()
);
// 使用集群
jedisCluster.set("key", "value");
String value = jedisCluster.get("key");
System.out.println(value);
// 关闭连接
jedisCluster.close();
}
}
3.8 Spring Boot集成
# application.yml
spring:
redis:
cluster:
nodes:
– 192.168.1.101:7000
– 192.168.1.102:7001
– 192.168.1.103:7002
– 192.168.1.104:7003
– 192.168.1.105:7004
– 192.168.1.106:7005
max-redirects: 3
password: 123456
lettuce:
pool:
max-active: 100
max-idle: 20
min-idle: 5
@Configuration
public class RedisClusterConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration()
.clusterNode("192.168.1.101", 7000)
.clusterNode("192.168.1.102", 7001)
.clusterNode("192.168.1.103", 7002)
.clusterNode("192.168.1.104", 7003)
.clusterNode("192.168.1.105", 7004)
.clusterNode("192.168.1.106", 7005);
clusterConfig.setMaxRedirects(3);
clusterConfig.setPassword("123456");
LettuceConnectionFactory factory = new LettuceConnectionFactory(
clusterConfig,
new LettucePoolingClientConfiguration()
);
return factory;
}
}
4. 两种模式对比
4.1 特性对比表
| 数据容量 | 单机内存限制 | 可水平扩展,理论无限 |
| 写能力 | 单节点写入 | 多节点并行写入 |
| 读能力 | 从节点分担 | 所有节点分担 |
| 延迟 | 低(单节点) | 中(可能跨节点) |
| 复杂度 | 低 | 高 |
| 多键操作 | 支持 | 有限支持(需同槽) |
| 事务支持 | 支持 | 有限支持(需同槽) |
| 适用场景 | 中小规模,高可用要求 | 大规模,高性能要求 |
4.2 选型决策树
#mermaid-svg-d7X8qzExWwG8GtFj{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-d7X8qzExWwG8GtFj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d7X8qzExWwG8GtFj .error-icon{fill:#552222;}#mermaid-svg-d7X8qzExWwG8GtFj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d7X8qzExWwG8GtFj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d7X8qzExWwG8GtFj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d7X8qzExWwG8GtFj .marker.cross{stroke:#333333;}#mermaid-svg-d7X8qzExWwG8GtFj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d7X8qzExWwG8GtFj p{margin:0;}#mermaid-svg-d7X8qzExWwG8GtFj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-d7X8qzExWwG8GtFj .cluster-label text{fill:#333;}#mermaid-svg-d7X8qzExWwG8GtFj .cluster-label span{color:#333;}#mermaid-svg-d7X8qzExWwG8GtFj .cluster-label span p{background-color:transparent;}#mermaid-svg-d7X8qzExWwG8GtFj .label text,#mermaid-svg-d7X8qzExWwG8GtFj span{fill:#333;color:#333;}#mermaid-svg-d7X8qzExWwG8GtFj .node rect,#mermaid-svg-d7X8qzExWwG8GtFj .node circle,#mermaid-svg-d7X8qzExWwG8GtFj .node ellipse,#mermaid-svg-d7X8qzExWwG8GtFj .node polygon,#mermaid-svg-d7X8qzExWwG8GtFj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-d7X8qzExWwG8GtFj .rough-node .label text,#mermaid-svg-d7X8qzExWwG8GtFj .node .label text,#mermaid-svg-d7X8qzExWwG8GtFj .image-shape .label,#mermaid-svg-d7X8qzExWwG8GtFj .icon-shape .label{text-anchor:middle;}#mermaid-svg-d7X8qzExWwG8GtFj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-d7X8qzExWwG8GtFj .rough-node .label,#mermaid-svg-d7X8qzExWwG8GtFj .node .label,#mermaid-svg-d7X8qzExWwG8GtFj .image-shape .label,#mermaid-svg-d7X8qzExWwG8GtFj .icon-shape .label{text-align:center;}#mermaid-svg-d7X8qzExWwG8GtFj .node.clickable{cursor:pointer;}#mermaid-svg-d7X8qzExWwG8GtFj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-d7X8qzExWwG8GtFj .arrowheadPath{fill:#333333;}#mermaid-svg-d7X8qzExWwG8GtFj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-d7X8qzExWwG8GtFj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-d7X8qzExWwG8GtFj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d7X8qzExWwG8GtFj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-d7X8qzExWwG8GtFj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d7X8qzExWwG8GtFj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-d7X8qzExWwG8GtFj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-d7X8qzExWwG8GtFj .cluster text{fill:#333;}#mermaid-svg-d7X8qzExWwG8GtFj .cluster span{color:#333;}#mermaid-svg-d7X8qzExWwG8GtFj 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-d7X8qzExWwG8GtFj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-d7X8qzExWwG8GtFj rect.text{fill:none;stroke-width:0;}#mermaid-svg-d7X8qzExWwG8GtFj .icon-shape,#mermaid-svg-d7X8qzExWwG8GtFj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-d7X8qzExWwG8GtFj .icon-shape p,#mermaid-svg-d7X8qzExWwG8GtFj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-d7X8qzExWwG8GtFj .icon-shape rect,#mermaid-svg-d7X8qzExWwG8GtFj .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-d7X8qzExWwG8GtFj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-d7X8qzExWwG8GtFj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-d7X8qzExWwG8GtFj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
小于100GB
大于100GB
小于10万
大于10万
需要自动故障转移
可接受手动恢复
是
否
开始选型
数据量预估
写QPS
集群模式
高可用要求
哨兵模式
主从复制
是否需要读写分离
哨兵+从节点
哨兵单节点
4.3 性能对比测试
@Component
public class PerformanceTest {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 测试哨兵模式性能
*/
public void testSentinelPerformance() {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
redisTemplate.opsForValue().set("key:" + i, "value:" + i);
}
long duration = System.currentTimeMillis() – start;
System.out.println("哨兵模式写入1万条耗时: " + duration + "ms");
}
/**
* 测试集群模式性能
*/
public void testClusterPerformance() {
long start = System.currentTimeMillis();
// 使用hash tag确保key分布到不同节点
for (int i = 0; i < 10000; i++) {
String node = "node" + (i % 3);
redisTemplate.opsForValue().set("{" + node + "}:key:" + i, "value:" + i);
}
long duration = System.currentTimeMillis() – start;
System.out.println("集群模式写入1万条耗时: " + duration + "ms");
}
}
5. 常见问题与解决方案
5.1 集群模式的限制
@Service
public class ClusterLimitationHandler {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 问题1:多键操作需要同槽
*/
public void multiKeyOperation() {
// 错误:不同槽位的key无法一起操作
// redisTemplate.opsForValue().multiGet(Arrays.asList("key1", "key2"));
// 正确:使用hash tag强制放入同一槽位
List<String> keys = Arrays.asList(
"{user:1001}:name",
"{user:1001}:age",
"{user:1001}:email"
);
List<String> values = redisTemplate.opsForValue().multiGet(keys);
}
/**
* 问题2:事务需要同槽
*/
public void transactionWithHashTag() {
// 开启事务
redisTemplate.setEnableTransactionSupport(true);
// 使用hash tag确保所有key在同一槽位
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.multi();
connection.set("{order:123}:status".getBytes(), "PAID".getBytes());
connection.set("{order:123}:time".getBytes(),
String.valueOf(System.currentTimeMillis()).getBytes());
connection.exec();
return null;
});
}
/**
* 问题3:Lua脚本需要同槽
*/
public void luaWithHashTag() {
// Lua脚本中所有key必须使用hash tag
String lua =
"local stock = redis.call('get', '{product:123}:stock') " +
"if tonumber(stock) > 0 then " +
" redis.call('decr', '{product:123}:stock') " +
" return 1 " +
"else " +
" return 0 " +
"end";
redisTemplate.execute(new DefaultRedisScript<>(lua, Long.class),
Collections.singletonList("{product:123}:stock"));
}
}
5.2 监控告警
@Component
public class ClusterMonitor {
@Autowired
private StringRedisTemplate redisTemplate;
private static final Logger log = LoggerFactory.getLogger(ClusterMonitor.class);
/**
* 监控集群状态
*/
@Scheduled(fixedDelay = 10000)
public void monitorCluster() {
// 获取集群节点信息
Object clusterInfo = redisTemplate.execute(
(RedisCallback<Object>) connection -> connection.info("cluster")
);
// 检查集群状态
String state = redisTemplate.opsForCluster().getClusterState();
if (!"ok".equalsIgnoreCase(state)) {
alertService.sendAlert("Redis集群状态异常: " + state);
}
// 监控槽位分布
Map<Integer, String> slotInfo = redisTemplate.opsForCluster().getSlotInfo();
// 检查是否有未分配的槽位
for (int i = 0; i < 16384; i++) {
if (!slotInfo.containsKey(i)) {
log.error("槽位未分配: {}", i);
alertService.sendAlert("Redis集群槽位缺失: " + i);
}
}
// 监控节点健康状态
Iterable<RedisClusterNode> nodes = redisTemplate.opsForCluster()
.getClusterNodes();
for (RedisClusterNode node : nodes) {
if (!node.isMaster() && node.getLinkState() != RedisClusterNode.LinkState.CONNECTED) {
alertService.sendAlert("从节点连接异常: " + node);
}
}
}
}
6. 选型建议
6.1 场景一:中小规模电商系统
/**
* 电商系统选型:哨兵模式
* 场景:商品数量10万,用户100万,QPS 5000
*/
@Configuration
public class EcommerceRedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("192.168.1.101", 26379)
.sentinel("192.168.1.102", 26379)
.sentinel("192.168.1.103", 26379);
sentinelConfig.setPassword("123456");
sentinelConfig.setDatabase(0);
LettuceConnectionFactory factory = new LettuceConnectionFactory(
sentinelConfig,
new LettucePoolingClientConfiguration()
);
return factory;
}
}
6.2 场景二:大型社交平台
/**
* 社交平台选型:集群模式
* 场景:用户数1亿,日活1000万,QPS 10万+
*/
@Configuration
public class SocialRedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration()
.clusterNode("192.168.1.101", 7000)
.clusterNode("192.168.1.102", 7001)
.clusterNode("192.168.1.103", 7002)
.clusterNode("192.168.1.104", 7003)
.clusterNode("192.168.1.105", 7004)
.clusterNode("192.168.1.106", 7005);
clusterConfig.setMaxRedirects(3);
clusterConfig.setPassword("123456");
LettuceConnectionFactory factory = new LettuceConnectionFactory(
clusterConfig,
new LettucePoolingClientConfiguration()
);
return factory;
}
/**
* 自定义Key生成器,支持Hash Tag
*/
@Bean
public KeyGenerator keyGenerator() {
return new HashTagKeyGenerator();
}
static class HashTagKeyGenerator implements KeyGenerator {
@Override
public String generate(String keyPrefix, Object... params) {
// 将用户ID作为Hash Tag
Long userId = (Long) params[0];
return "{" + userId + "}:" + keyPrefix + ":" + userId;
}
}
}
7. 总结
7.1 两种模式的本质区别
#mermaid-svg-tiElsnYkWtYeKXx1{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-tiElsnYkWtYeKXx1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tiElsnYkWtYeKXx1 .error-icon{fill:#552222;}#mermaid-svg-tiElsnYkWtYeKXx1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tiElsnYkWtYeKXx1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tiElsnYkWtYeKXx1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tiElsnYkWtYeKXx1 .marker.cross{stroke:#333333;}#mermaid-svg-tiElsnYkWtYeKXx1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tiElsnYkWtYeKXx1 p{margin:0;}#mermaid-svg-tiElsnYkWtYeKXx1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 .cluster-label text{fill:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 .cluster-label span{color:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 .cluster-label span p{background-color:transparent;}#mermaid-svg-tiElsnYkWtYeKXx1 .label text,#mermaid-svg-tiElsnYkWtYeKXx1 span{fill:#333;color:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 .node rect,#mermaid-svg-tiElsnYkWtYeKXx1 .node circle,#mermaid-svg-tiElsnYkWtYeKXx1 .node ellipse,#mermaid-svg-tiElsnYkWtYeKXx1 .node polygon,#mermaid-svg-tiElsnYkWtYeKXx1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tiElsnYkWtYeKXx1 .rough-node .label text,#mermaid-svg-tiElsnYkWtYeKXx1 .node .label text,#mermaid-svg-tiElsnYkWtYeKXx1 .image-shape .label,#mermaid-svg-tiElsnYkWtYeKXx1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-tiElsnYkWtYeKXx1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tiElsnYkWtYeKXx1 .rough-node .label,#mermaid-svg-tiElsnYkWtYeKXx1 .node .label,#mermaid-svg-tiElsnYkWtYeKXx1 .image-shape .label,#mermaid-svg-tiElsnYkWtYeKXx1 .icon-shape .label{text-align:center;}#mermaid-svg-tiElsnYkWtYeKXx1 .node.clickable{cursor:pointer;}#mermaid-svg-tiElsnYkWtYeKXx1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tiElsnYkWtYeKXx1 .arrowheadPath{fill:#333333;}#mermaid-svg-tiElsnYkWtYeKXx1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tiElsnYkWtYeKXx1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tiElsnYkWtYeKXx1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tiElsnYkWtYeKXx1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tiElsnYkWtYeKXx1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tiElsnYkWtYeKXx1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tiElsnYkWtYeKXx1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tiElsnYkWtYeKXx1 .cluster text{fill:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 .cluster span{color:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 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-tiElsnYkWtYeKXx1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tiElsnYkWtYeKXx1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-tiElsnYkWtYeKXx1 .icon-shape,#mermaid-svg-tiElsnYkWtYeKXx1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tiElsnYkWtYeKXx1 .icon-shape p,#mermaid-svg-tiElsnYkWtYeKXx1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tiElsnYkWtYeKXx1 .icon-shape rect,#mermaid-svg-tiElsnYkWtYeKXx1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tiElsnYkWtYeKXx1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tiElsnYkWtYeKXx1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tiElsnYkWtYeKXx1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
集群模式
多主节点
写能力水平扩展
数据量水平扩展
多从节点
读能力水平扩展
自动故障转移
哨兵模式
单主节点
写能力有限
数据量有限
从节点
读扩展
哨兵
高可用
7.2 核心观点
7.3 最终建议
| 数据量 < 100GB,QPS < 5万 | 哨兵模式 | 简单稳定,足够使用 |
| 数据量 100GB-1TB,QPS 5-10万 | 集群模式 | 需要水平扩展 |
| 数据量 > 1TB,QPS > 10万 | 集群模式 | 必须分片 |
| 对多键操作要求高 | 哨兵模式 | 集群模式有限制 |
| 追求极致性能 | 集群模式 | 多节点并行 |
记住:架构设计不是选择题,而是分析题。深入理解业务需求,才能做出正确的技术选型。
|
🌺The End🌺点点关注,收藏不迷路🌺 |




