欢迎光临
我们一直在努力

Redis分布式模式深度解析:哨兵模式与集群模式全对比

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 核心观点

  • 哨兵模式解决的是"高可用"问题,适合中小规模系统
  • 集群模式解决的是"高性能"问题,适合大规模系统
  • 没有最好的模式,只有最适合的模式
  • 架构选型要考虑当前规模和未来3-5年的增长
  • 监控和运维同样重要
  • 7.3 最终建议

    场景推荐模式原因
    数据量 < 100GB,QPS < 5万 哨兵模式 简单稳定,足够使用
    数据量 100GB-1TB,QPS 5-10万 集群模式 需要水平扩展
    数据量 > 1TB,QPS > 10万 集群模式 必须分片
    对多键操作要求高 哨兵模式 集群模式有限制
    追求极致性能 集群模式 多节点并行

    记住:架构设计不是选择题,而是分析题。深入理解业务需求,才能做出正确的技术选型。


    🌺The End🌺点点关注,收藏不迷路🌺

    赞(0)
    未经允许不得转载:171主机测评 » Redis分布式模式深度解析:哨兵模式与集群模式全对比
    分享到: 更多 (0)

    评论 抢沙发

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