文章目录
- 前言
- 一、Redis主从复制架构
-
- 1.1 架构原理
- 1.2 核心机制详解
-
- 1.2.1 复制过程
- 1.2.2 断线续传机制
- 1.3 生产环境痛点
-
- 痛点1:主从延迟导致的数据不一致
- 痛点2:复制缓冲区溢出
- 二、哨兵模式深度剖析
-
- 2.1 架构演进
- 2.2 核心功能实现
-
- 2.2.1 监控机制
- 2.2.2 故障转移流程
- 2.3 生产环境脑裂实战案例
- 三、Redis Cluster集群架构
-
- 3.1 架构设计
- 3.2 核心机制详解
-
- 3.2.1 槽位计算原理
- 3.2.2 批量操作优化实战
-
- 场景1:用户维度的批量查询
- 场景2:多维度数据关联
- 3.3 集群故障转移实战
-
- 3.3.1 故障检测机制
- 3.3.2 故障转移源码级分析
- 四、生产环境选型实战指南
-
- 4.1 业务场景1:中小型电商系统
- 4.2 业务场景2:大型社交平台
- 4.3 业务场景3:金融支付系统
- 五、架构选型决策树
- 六、总结与最佳实践
-
- 6.1 各架构适用场景速查表
- 6.2 生产环境避坑指南
- 6.3 性能调优清单
前言
作为一名Java后端工程师,我在8年的职业生涯中经历了多次Redis架构演进。从最初的单机版,到主从复制,再到哨兵模式,最后演进到Cluster集群,每一次升级都伴随着业务的指数级增长。本文将结合实战经验,深入剖析这三种架构的核心原理、生产问题及选型策略。
一、Redis主从复制架构
1.1 架构原理
主从复制是实现Redis高可用的基础架构。一个Master节点负责写操作,多个Slave节点负责读操作,数据通过异步复制从Master同步到Slave。
#mermaid-svg-C8NRcvYWEoAgxfLZ{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-C8NRcvYWEoAgxfLZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-C8NRcvYWEoAgxfLZ .error-icon{fill:#552222;}#mermaid-svg-C8NRcvYWEoAgxfLZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-C8NRcvYWEoAgxfLZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .marker.cross{stroke:#333333;}#mermaid-svg-C8NRcvYWEoAgxfLZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-C8NRcvYWEoAgxfLZ p{margin:0;}#mermaid-svg-C8NRcvYWEoAgxfLZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .cluster-label text{fill:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .cluster-label span{color:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .cluster-label span p{background-color:transparent;}#mermaid-svg-C8NRcvYWEoAgxfLZ .label text,#mermaid-svg-C8NRcvYWEoAgxfLZ span{fill:#333;color:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .node rect,#mermaid-svg-C8NRcvYWEoAgxfLZ .node circle,#mermaid-svg-C8NRcvYWEoAgxfLZ .node ellipse,#mermaid-svg-C8NRcvYWEoAgxfLZ .node polygon,#mermaid-svg-C8NRcvYWEoAgxfLZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .rough-node .label text,#mermaid-svg-C8NRcvYWEoAgxfLZ .node .label text,#mermaid-svg-C8NRcvYWEoAgxfLZ .image-shape .label,#mermaid-svg-C8NRcvYWEoAgxfLZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-C8NRcvYWEoAgxfLZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .rough-node .label,#mermaid-svg-C8NRcvYWEoAgxfLZ .node .label,#mermaid-svg-C8NRcvYWEoAgxfLZ .image-shape .label,#mermaid-svg-C8NRcvYWEoAgxfLZ .icon-shape .label{text-align:center;}#mermaid-svg-C8NRcvYWEoAgxfLZ .node.clickable{cursor:pointer;}#mermaid-svg-C8NRcvYWEoAgxfLZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .arrowheadPath{fill:#333333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C8NRcvYWEoAgxfLZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-C8NRcvYWEoAgxfLZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C8NRcvYWEoAgxfLZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-C8NRcvYWEoAgxfLZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .cluster text{fill:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ .cluster span{color:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ 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-C8NRcvYWEoAgxfLZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-C8NRcvYWEoAgxfLZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-C8NRcvYWEoAgxfLZ .icon-shape,#mermaid-svg-C8NRcvYWEoAgxfLZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C8NRcvYWEoAgxfLZ .icon-shape p,#mermaid-svg-C8NRcvYWEoAgxfLZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-C8NRcvYWEoAgxfLZ .icon-shape rect,#mermaid-svg-C8NRcvYWEoAgxfLZ .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C8NRcvYWEoAgxfLZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-C8NRcvYWEoAgxfLZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-C8NRcvYWEoAgxfLZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
复制原理
ReplicationID复制ID
Offset复制偏移量
环形缓冲区积压队列
客户端
主节点读写操作
全量/增量同步
从节点2只读
从节点3只读
1.2 核心机制详解
1.2.1 复制过程
# 从节点执行命令
SLAVEOF 192.168.1.100 6379
# 复制过程三个阶段:
1. 连接建立:从节点保存主节点信息
2. 全量复制:主节点生成RDB发送给从节点
3. 增量复制:主节点持续发送写命令
1.2.2 断线续传机制
Master
Slave
Master
Slave
#mermaid-svg-UcQrChy0nve5xLjV{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-UcQrChy0nve5xLjV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UcQrChy0nve5xLjV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UcQrChy0nve5xLjV .error-icon{fill:#552222;}#mermaid-svg-UcQrChy0nve5xLjV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UcQrChy0nve5xLjV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UcQrChy0nve5xLjV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UcQrChy0nve5xLjV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UcQrChy0nve5xLjV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UcQrChy0nve5xLjV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UcQrChy0nve5xLjV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UcQrChy0nve5xLjV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UcQrChy0nve5xLjV .marker.cross{stroke:#333333;}#mermaid-svg-UcQrChy0nve5xLjV svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UcQrChy0nve5xLjV p{margin:0;}#mermaid-svg-UcQrChy0nve5xLjV .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UcQrChy0nve5xLjV text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-UcQrChy0nve5xLjV .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UcQrChy0nve5xLjV .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-UcQrChy0nve5xLjV .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-UcQrChy0nve5xLjV .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-UcQrChy0nve5xLjV #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-UcQrChy0nve5xLjV .sequenceNumber{fill:white;}#mermaid-svg-UcQrChy0nve5xLjV #sequencenumber{fill:#333;}#mermaid-svg-UcQrChy0nve5xLjV #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-UcQrChy0nve5xLjV .messageText{fill:#333;stroke:none;}#mermaid-svg-UcQrChy0nve5xLjV .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UcQrChy0nve5xLjV .labelText,#mermaid-svg-UcQrChy0nve5xLjV .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-UcQrChy0nve5xLjV .loopText,#mermaid-svg-UcQrChy0nve5xLjV .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-UcQrChy0nve5xLjV .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-UcQrChy0nve5xLjV .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-UcQrChy0nve5xLjV .noteText,#mermaid-svg-UcQrChy0nve5xLjV .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-UcQrChy0nve5xLjV .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UcQrChy0nve5xLjV .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UcQrChy0nve5xLjV .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UcQrChy0nve5xLjV .actorPopupMenu{position:absolute;}#mermaid-svg-UcQrChy0nve5xLjV .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-UcQrChy0nve5xLjV .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UcQrChy0nve5xLjV .actor-man circle,#mermaid-svg-UcQrChy0nve5xLjV line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-UcQrChy0nve5xLjV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
正常复制中
网络断开
重新连接
alt
[offset在积压队列内]
[offset已过期]
REPLCONF ACK {offset}
PSYNC {replid} {offset}
检查offset是否在积压队列
CONTINUE
发送积压数据
FULLRESYNC {new_replid}
全量同步RDB
1.3 生产环境痛点
痛点1:主从延迟导致的数据不一致
// 场景:用户下单后立即查询订单
public Order createOrder(Order order) {
redisTemplate.opsForValue().set("order:" + order.getId(), order);
// 立即查询,可能路由到从库
Order queryOrder = redisTemplate.opsForValue().get("order:" + order.getId());
if (queryOrder == null) {
// 数据还没同步到从库
throw new BusinessException("订单创建失败,请重试");
}
return queryOrder;
}
// 解决方案:强制读主库
@RedisRoute(master = true)
public Order getOrder(String orderId) {
return redisTemplate.opsForValue().get("order:" + orderId);
}
痛点2:复制缓冲区溢出
# 配置优化
# redis.conf
repl-backlog-size 256mb # 默认1MB,建议增大
client-output-buffer-limit slave 256mb 64mb 60 # 从节点缓冲区限制
二、哨兵模式深度剖析
2.1 架构演进
为了解决主从架构无法自动故障转移的问题,Redis 2.8引入了哨兵机制。哨兵本身也是一个集群,通常部署3个或5个奇数节点。
#mermaid-svg-OL2rvXDotKIPBD2a{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-OL2rvXDotKIPBD2a .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OL2rvXDotKIPBD2a .error-icon{fill:#552222;}#mermaid-svg-OL2rvXDotKIPBD2a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OL2rvXDotKIPBD2a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OL2rvXDotKIPBD2a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OL2rvXDotKIPBD2a .marker.cross{stroke:#333333;}#mermaid-svg-OL2rvXDotKIPBD2a svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OL2rvXDotKIPBD2a p{margin:0;}#mermaid-svg-OL2rvXDotKIPBD2a .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-OL2rvXDotKIPBD2a .cluster-label text{fill:#333;}#mermaid-svg-OL2rvXDotKIPBD2a .cluster-label span{color:#333;}#mermaid-svg-OL2rvXDotKIPBD2a .cluster-label span p{background-color:transparent;}#mermaid-svg-OL2rvXDotKIPBD2a .label text,#mermaid-svg-OL2rvXDotKIPBD2a span{fill:#333;color:#333;}#mermaid-svg-OL2rvXDotKIPBD2a .node rect,#mermaid-svg-OL2rvXDotKIPBD2a .node circle,#mermaid-svg-OL2rvXDotKIPBD2a .node ellipse,#mermaid-svg-OL2rvXDotKIPBD2a .node polygon,#mermaid-svg-OL2rvXDotKIPBD2a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OL2rvXDotKIPBD2a .rough-node .label text,#mermaid-svg-OL2rvXDotKIPBD2a .node .label text,#mermaid-svg-OL2rvXDotKIPBD2a .image-shape .label,#mermaid-svg-OL2rvXDotKIPBD2a .icon-shape .label{text-anchor:middle;}#mermaid-svg-OL2rvXDotKIPBD2a .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OL2rvXDotKIPBD2a .rough-node .label,#mermaid-svg-OL2rvXDotKIPBD2a .node .label,#mermaid-svg-OL2rvXDotKIPBD2a .image-shape .label,#mermaid-svg-OL2rvXDotKIPBD2a .icon-shape .label{text-align:center;}#mermaid-svg-OL2rvXDotKIPBD2a .node.clickable{cursor:pointer;}#mermaid-svg-OL2rvXDotKIPBD2a .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OL2rvXDotKIPBD2a .arrowheadPath{fill:#333333;}#mermaid-svg-OL2rvXDotKIPBD2a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OL2rvXDotKIPBD2a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OL2rvXDotKIPBD2a .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OL2rvXDotKIPBD2a .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OL2rvXDotKIPBD2a .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OL2rvXDotKIPBD2a .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OL2rvXDotKIPBD2a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OL2rvXDotKIPBD2a .cluster text{fill:#333;}#mermaid-svg-OL2rvXDotKIPBD2a .cluster span{color:#333;}#mermaid-svg-OL2rvXDotKIPBD2a 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-OL2rvXDotKIPBD2a .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OL2rvXDotKIPBD2a rect.text{fill:none;stroke-width:0;}#mermaid-svg-OL2rvXDotKIPBD2a .icon-shape,#mermaid-svg-OL2rvXDotKIPBD2a .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OL2rvXDotKIPBD2a .icon-shape p,#mermaid-svg-OL2rvXDotKIPBD2a .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OL2rvXDotKIPBD2a .icon-shape rect,#mermaid-svg-OL2rvXDotKIPBD2a .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OL2rvXDotKIPBD2a .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OL2rvXDotKIPBD2a .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OL2rvXDotKIPBD2a :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Redis节点
哨兵集群
Sentinel-1192.168.1.10:26379
Sentinel-2192.168.1.11:26379
Sentinel-3192.168.1.12:26379
Master192.168.1.100:6379
Slave-1192.168.1.101:6379
Slave-2192.168.1.102:6379
客户端通过哨兵发现主节点
2.2 核心功能实现
2.2.1 监控机制
# 哨兵配置
sentinel monitor mymaster 192.168.1.100 6379 2 # 2表示需要2个哨兵同意
sentinel down-after-milliseconds mymaster 30000 # 30秒无响应判定为下线
sentinel failover-timeout mymaster 180000 # 故障转移超时时间
sentinel parallel-syncs mymaster 1 # 同时同步的从节点数量
2.2.2 故障转移流程
Client
其他Slave
Slave节点
Master(故障)
Sentinel-3
Sentinel-2
Sentinel-1
Client
其他Slave
Slave节点
Master(故障)
Sentinel-3
Sentinel-2
Sentinel-1
#mermaid-svg-sGDI0oTWbpQ5Xxmq{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-sGDI0oTWbpQ5Xxmq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .error-icon{fill:#552222;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .marker.cross{stroke:#333333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sGDI0oTWbpQ5Xxmq p{margin:0;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sGDI0oTWbpQ5Xxmq text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-sGDI0oTWbpQ5Xxmq .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .sequenceNumber{fill:white;}#mermaid-svg-sGDI0oTWbpQ5Xxmq #sequencenumber{fill:#333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .messageText{fill:#333;stroke:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .labelText,#mermaid-svg-sGDI0oTWbpQ5Xxmq .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .loopText,#mermaid-svg-sGDI0oTWbpQ5Xxmq .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .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-sGDI0oTWbpQ5Xxmq .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .noteText,#mermaid-svg-sGDI0oTWbpQ5Xxmq .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .actorPopupMenu{position:absolute;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .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-sGDI0oTWbpQ5Xxmq .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-sGDI0oTWbpQ5Xxmq .actor-man circle,#mermaid-svg-sGDI0oTWbpQ5Xxmq line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-sGDI0oTWbpQ5Xxmq :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
阶段1:主观下线
网络分区
sdown
阶段2:客观下线
odown(达到quorum=2)
阶段3:选举Leader
成为Leader
阶段4:选举新Master
过滤掉不健康的节点
优先级最高的成为新Master
阶段5:重新配置
PING
超时
SENTINEL is-master-down-by-addr
SENTINEL is-master-down-by-addr
同意下线
同意下线
请求投票
投票给S1
投票给S1
获取从节点列表
slaveof no one
成为新Master
slaveof 新Master
发布配置变更通知
2.3 生产环境脑裂实战案例
案例背景 某电商公司使用Redis哨兵架构,双11大促期间发生网络抖动,导致主节点与哨兵集群网络分区。
问题复现
#mermaid-svg-4b9B3OsTFfNWkEgw{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-4b9B3OsTFfNWkEgw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4b9B3OsTFfNWkEgw .error-icon{fill:#552222;}#mermaid-svg-4b9B3OsTFfNWkEgw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4b9B3OsTFfNWkEgw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4b9B3OsTFfNWkEgw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4b9B3OsTFfNWkEgw .marker.cross{stroke:#333333;}#mermaid-svg-4b9B3OsTFfNWkEgw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4b9B3OsTFfNWkEgw p{margin:0;}#mermaid-svg-4b9B3OsTFfNWkEgw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw .cluster-label text{fill:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw .cluster-label span{color:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw .cluster-label span p{background-color:transparent;}#mermaid-svg-4b9B3OsTFfNWkEgw .label text,#mermaid-svg-4b9B3OsTFfNWkEgw span{fill:#333;color:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw .node rect,#mermaid-svg-4b9B3OsTFfNWkEgw .node circle,#mermaid-svg-4b9B3OsTFfNWkEgw .node ellipse,#mermaid-svg-4b9B3OsTFfNWkEgw .node polygon,#mermaid-svg-4b9B3OsTFfNWkEgw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4b9B3OsTFfNWkEgw .rough-node .label text,#mermaid-svg-4b9B3OsTFfNWkEgw .node .label text,#mermaid-svg-4b9B3OsTFfNWkEgw .image-shape .label,#mermaid-svg-4b9B3OsTFfNWkEgw .icon-shape .label{text-anchor:middle;}#mermaid-svg-4b9B3OsTFfNWkEgw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4b9B3OsTFfNWkEgw .rough-node .label,#mermaid-svg-4b9B3OsTFfNWkEgw .node .label,#mermaid-svg-4b9B3OsTFfNWkEgw .image-shape .label,#mermaid-svg-4b9B3OsTFfNWkEgw .icon-shape .label{text-align:center;}#mermaid-svg-4b9B3OsTFfNWkEgw .node.clickable{cursor:pointer;}#mermaid-svg-4b9B3OsTFfNWkEgw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4b9B3OsTFfNWkEgw .arrowheadPath{fill:#333333;}#mermaid-svg-4b9B3OsTFfNWkEgw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4b9B3OsTFfNWkEgw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4b9B3OsTFfNWkEgw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4b9B3OsTFfNWkEgw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4b9B3OsTFfNWkEgw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4b9B3OsTFfNWkEgw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4b9B3OsTFfNWkEgw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4b9B3OsTFfNWkEgw .cluster text{fill:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw .cluster span{color:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw 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-4b9B3OsTFfNWkEgw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4b9B3OsTFfNWkEgw rect.text{fill:none;stroke-width:0;}#mermaid-svg-4b9B3OsTFfNWkEgw .icon-shape,#mermaid-svg-4b9B3OsTFfNWkEgw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4b9B3OsTFfNWkEgw .icon-shape p,#mermaid-svg-4b9B3OsTFfNWkEgw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4b9B3OsTFfNWkEgw .icon-shape rect,#mermaid-svg-4b9B3OsTFfNWkEgw .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4b9B3OsTFfNWkEgw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4b9B3OsTFfNWkEgw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4b9B3OsTFfNWkEgw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
交换机B区域
交换机A区域
原Master仍在运行
客户端1继续写入
Sentinel-1
Sentinel-2
Sentinel-3
新选举的Master
客户端2写入新主
网络中断10分钟
数据丢失分析
// 原Master上10分钟的写入量
// 假设QPS=5000,10分钟=300万条数据全部丢失
// 大促期间用户投诉:订单状态不一致、优惠券使用记录丢失
// 业务损失:约50万订单数据异常
解决方案
# 哨兵配置优化
sentinel down-after-milliseconds mymaster 10000 # 缩短检测时间,从30s改为10s
# Redis配置优化
min-slaves-to-write 2 # 至少2个从节点在线才能写入
min-slaves-max-lag 5 # 从节点延迟不超过5秒
# 应用层优化
@Component
public class RedisWriteGuard {
@Value("${redis.master.count}")
private int expectedMasterCount = 1;
public void writeWithGuard(String key, Object value) {
// 检查当前Redis实例是否是真正的Master
String role = redisTemplate.opsForValue().get("redis:role:check");
if (role == null) {
// 可能是脑裂产生的Master,拒绝写入
throw new RedisBrainSplitException("检测到脑裂风险,拒绝写入");
}
// 正常写入
redisTemplate.opsForValue().set(key, value);
}
}
三、Redis Cluster集群架构
3.1 架构设计
Redis 3.0推出的分布式解决方案,通过数据分片解决单机容量和写压力问题。
#mermaid-svg-wPOIijpXEc5KSg5e{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-wPOIijpXEc5KSg5e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wPOIijpXEc5KSg5e .error-icon{fill:#552222;}#mermaid-svg-wPOIijpXEc5KSg5e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wPOIijpXEc5KSg5e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wPOIijpXEc5KSg5e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wPOIijpXEc5KSg5e .marker.cross{stroke:#333333;}#mermaid-svg-wPOIijpXEc5KSg5e svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wPOIijpXEc5KSg5e p{margin:0;}#mermaid-svg-wPOIijpXEc5KSg5e .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-wPOIijpXEc5KSg5e .cluster-label text{fill:#333;}#mermaid-svg-wPOIijpXEc5KSg5e .cluster-label span{color:#333;}#mermaid-svg-wPOIijpXEc5KSg5e .cluster-label span p{background-color:transparent;}#mermaid-svg-wPOIijpXEc5KSg5e .label text,#mermaid-svg-wPOIijpXEc5KSg5e span{fill:#333;color:#333;}#mermaid-svg-wPOIijpXEc5KSg5e .node rect,#mermaid-svg-wPOIijpXEc5KSg5e .node circle,#mermaid-svg-wPOIijpXEc5KSg5e .node ellipse,#mermaid-svg-wPOIijpXEc5KSg5e .node polygon,#mermaid-svg-wPOIijpXEc5KSg5e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wPOIijpXEc5KSg5e .rough-node .label text,#mermaid-svg-wPOIijpXEc5KSg5e .node .label text,#mermaid-svg-wPOIijpXEc5KSg5e .image-shape .label,#mermaid-svg-wPOIijpXEc5KSg5e .icon-shape .label{text-anchor:middle;}#mermaid-svg-wPOIijpXEc5KSg5e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wPOIijpXEc5KSg5e .rough-node .label,#mermaid-svg-wPOIijpXEc5KSg5e .node .label,#mermaid-svg-wPOIijpXEc5KSg5e .image-shape .label,#mermaid-svg-wPOIijpXEc5KSg5e .icon-shape .label{text-align:center;}#mermaid-svg-wPOIijpXEc5KSg5e .node.clickable{cursor:pointer;}#mermaid-svg-wPOIijpXEc5KSg5e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wPOIijpXEc5KSg5e .arrowheadPath{fill:#333333;}#mermaid-svg-wPOIijpXEc5KSg5e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wPOIijpXEc5KSg5e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wPOIijpXEc5KSg5e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wPOIijpXEc5KSg5e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wPOIijpXEc5KSg5e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wPOIijpXEc5KSg5e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wPOIijpXEc5KSg5e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wPOIijpXEc5KSg5e .cluster text{fill:#333;}#mermaid-svg-wPOIijpXEc5KSg5e .cluster span{color:#333;}#mermaid-svg-wPOIijpXEc5KSg5e 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-wPOIijpXEc5KSg5e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wPOIijpXEc5KSg5e rect.text{fill:none;stroke-width:0;}#mermaid-svg-wPOIijpXEc5KSg5e .icon-shape,#mermaid-svg-wPOIijpXEc5KSg5e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wPOIijpXEc5KSg5e .icon-shape p,#mermaid-svg-wPOIijpXEc5KSg5e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wPOIijpXEc5KSg5e .icon-shape rect,#mermaid-svg-wPOIijpXEc5KSg5e .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wPOIijpXEc5KSg5e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wPOIijpXEc5KSg5e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wPOIijpXEc5KSg5e :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Cluster节点
Node1192.168.1.1:6379槽位:0-2730
Node2192.168.1.2:6379槽位:2731-5460
Node3192.168.1.3:6379槽位:5461-8190
Node4192.168.1.4:6379槽位:8191-10920
Node5192.168.1.5:6379槽位:10921-13650
Node6192.168.1.6:6379槽位:13651-16383
Node7Slave
Node8Slave
Node9Slave
Smart ClientJedisCluster/lettuce
3.2 核心机制详解
3.2.1 槽位计算原理
public class RedisClusterSlotCalculator {
// 16384个槽位
private static final int SLOT_COUNT = 16384;
/**
* 计算key的槽位
* CRC16(key) & 16383
*/
public static int calculateSlot(String key) {
// 处理Hash Tag
String hashKey = getHashKey(key);
// CRC16算法实现
int crc = CRC16.crc16(hashKey.getBytes());
return crc & (SLOT_COUNT – 1);
}
/**
* 提取Hash Tag
* 规则:只计算第一个{和}之间的内容
*/
private static String getHashKey(String key) {
int s = key.indexOf("{");
if (s >= 0) {
int e = key.indexOf("}", s + 1);
if (e >= 0 && e != s + 1) {
return key.substring(s + 1, e);
}
}
return key;
}
}
3.2.2 批量操作优化实战
场景1:用户维度的批量查询
@Service
public class UserDataService {
/**
* 错误示例:key分散在不同节点
* 导致网络开销:3次网络请求
*/
public Map<String, Object> getUserDataBad(String userId) {
Map<String, Object> result = new HashMap<>();
// 这三个key可能分布在3个不同节点
result.put("profile", redisTemplate.opsForValue().get("user:" + userId + ":profile"));
result.put("orders", redisTemplate.opsForValue().get("user:" + userId + ":orders"));
result.put("points", redisTemplate.opsForValue().get("user:" + userId + ":points"));
return result;
}
/**
* 正确示例:使用Hash Tag
* 所有key在同一个节点,1次网络请求
*/
public List<Object> getUserDataGood(String userId) {
String hashTag = "{user:" + userId + "}";
List<String> keys = Arrays.asList(
hashTag + ":profile",
hashTag + ":orders",
hashTag + ":points"
);
// mget保证在一个节点执行
return redisTemplate.opsForValue().multiGet(keys);
}
/**
* 批量写入优化
*/
public void batchUpdateUserData(String userId, UserData data) {
String hashTag = "{user:" + userId + "}";
Map<String, Object> dataMap = new HashMap<>();
dataMap.put(hashTag + ":profile", data.getProfile());
dataMap.put(hashTag + ":orders", data.getOrders());
dataMap.put(hashTag + ":points", data.getPoints());
// 使用pipeline批量写入
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
dataMap.forEach((key, value) -> {
connection.set(key.getBytes(),
SerializationUtils.serialize(value));
});
return null;
});
}
}
场景2:多维度数据关联
/**
* 电商订单维度查询优化
*/
@Component
public class OrderOptimizer {
/**
* 订单相关的多个维度
* 使用复合Hash Tag设计key
*/
public void storeOrderData(Order order) {
String orderTag = "{order:" + order.getOrderId() + "}";
// 所有相关key都在同一槽位
String orderKey = orderTag + ":basic";
String itemsKey = orderTag + ":items";
String logisKey = orderTag + ":logistics";
String paymentKey = orderTag + ":payment";
// pipeline批量写入
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
connection.set(orderKey.getBytes(), order.toString().getBytes());
connection.set(itemsKey.getBytes(), order.getItems().toString().getBytes());
connection.set(logisKey.getBytes(), order.getLogistics().toString().getBytes());
connection.set(paymentKey.getBytes(), order.getPayment().toString().getBytes());
return null;
});
}
/**
* 批量查询订单数据
*/
public OrderVO getOrderDetail(String orderId) {
String orderTag = "{order:" + orderId + "}";
List<String> keys = Arrays.asList(
orderTag + ":basic",
orderTag + ":items",
orderTag + ":logistics",
orderTag + ":payment"
);
List<Object> values = redisTemplate.opsForValue().multiGet(keys);
// 组装数据
return assembleOrderVO(values);
}
}
3.3 集群故障转移实战
3.3.1 故障检测机制
#mermaid-svg-9qOjKVPxtu98d33e{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-9qOjKVPxtu98d33e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9qOjKVPxtu98d33e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9qOjKVPxtu98d33e .error-icon{fill:#552222;}#mermaid-svg-9qOjKVPxtu98d33e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9qOjKVPxtu98d33e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9qOjKVPxtu98d33e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9qOjKVPxtu98d33e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9qOjKVPxtu98d33e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9qOjKVPxtu98d33e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9qOjKVPxtu98d33e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9qOjKVPxtu98d33e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9qOjKVPxtu98d33e .marker.cross{stroke:#333333;}#mermaid-svg-9qOjKVPxtu98d33e svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9qOjKVPxtu98d33e p{margin:0;}#mermaid-svg-9qOjKVPxtu98d33e .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9qOjKVPxtu98d33e .cluster-label text{fill:#333;}#mermaid-svg-9qOjKVPxtu98d33e .cluster-label span{color:#333;}#mermaid-svg-9qOjKVPxtu98d33e .cluster-label span p{background-color:transparent;}#mermaid-svg-9qOjKVPxtu98d33e .label text,#mermaid-svg-9qOjKVPxtu98d33e span{fill:#333;color:#333;}#mermaid-svg-9qOjKVPxtu98d33e .node rect,#mermaid-svg-9qOjKVPxtu98d33e .node circle,#mermaid-svg-9qOjKVPxtu98d33e .node ellipse,#mermaid-svg-9qOjKVPxtu98d33e .node polygon,#mermaid-svg-9qOjKVPxtu98d33e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9qOjKVPxtu98d33e .rough-node .label text,#mermaid-svg-9qOjKVPxtu98d33e .node .label text,#mermaid-svg-9qOjKVPxtu98d33e .image-shape .label,#mermaid-svg-9qOjKVPxtu98d33e .icon-shape .label{text-anchor:middle;}#mermaid-svg-9qOjKVPxtu98d33e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9qOjKVPxtu98d33e .rough-node .label,#mermaid-svg-9qOjKVPxtu98d33e .node .label,#mermaid-svg-9qOjKVPxtu98d33e .image-shape .label,#mermaid-svg-9qOjKVPxtu98d33e .icon-shape .label{text-align:center;}#mermaid-svg-9qOjKVPxtu98d33e .node.clickable{cursor:pointer;}#mermaid-svg-9qOjKVPxtu98d33e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9qOjKVPxtu98d33e .arrowheadPath{fill:#333333;}#mermaid-svg-9qOjKVPxtu98d33e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9qOjKVPxtu98d33e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9qOjKVPxtu98d33e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9qOjKVPxtu98d33e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9qOjKVPxtu98d33e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9qOjKVPxtu98d33e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9qOjKVPxtu98d33e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9qOjKVPxtu98d33e .cluster text{fill:#333;}#mermaid-svg-9qOjKVPxtu98d33e .cluster span{color:#333;}#mermaid-svg-9qOjKVPxtu98d33e 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-9qOjKVPxtu98d33e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9qOjKVPxtu98d33e rect.text{fill:none;stroke-width:0;}#mermaid-svg-9qOjKVPxtu98d33e .icon-shape,#mermaid-svg-9qOjKVPxtu98d33e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9qOjKVPxtu98d33e .icon-shape p,#mermaid-svg-9qOjKVPxtu98d33e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9qOjKVPxtu98d33e .icon-shape rect,#mermaid-svg-9qOjKVPxtu98d33e .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9qOjKVPxtu98d33e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9qOjKVPxtu98d33e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9qOjKVPxtu98d33e :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
故障检测流程
PING
PONG
PING超时
Gossip广播
Gossip广播
确认故障
确认故障
节点A
节点B
节点C
节点D
PFAIL状态
节点E
节点F
FAIL状态达成开始故障转移
3.3.2 故障转移源码级分析
// 模拟集群故障转移过程
public class ClusterFailoverSimulator {
/**
* 故障转移触发条件
*/
class FailoverCondition {
// 节点超时时间,默认15000ms
int clusterNodeTimeout = 15000;
// 主观下线阈值
int pfailCount = 1;
// 客观下线所需确认数
int failReportCount = clusterSize / 2 + 1;
}
/**
* 从节点选举算法
*/
class SlaveElection {
/**
* 优先级计算:
* 1. 复制偏移量越大优先级越高
* 2. 节点ID按字母排序
*/
public Slave selectNewMaster(List<Slave> slaves) {
return slaves.stream()
.filter(Slave::isHealthy)
.max(Comparator.comparing(Slave::getReplicationOffset)
.thenComparing(Slave::getNodeId))
.orElseThrow(() -> new NoMasterElectedException());
}
/**
* 投票机制
* 每个Master节点只有一票
* 获得半数以上票数的Slave成为新Master
*/
public boolean election(Slave candidate, int masterCount) {
int votes = candidate.requestVotes();
return votes > masterCount / 2;
}
}
}
四、生产环境选型实战指南
4.1 业务场景1:中小型电商系统
业务特征:
- 数据量:50GB以内
- QPS:读写混合5-10万
- 可用性要求:99.95%
- 预算:有限
架构方案:
部署架构:
1主2从 + 3哨兵哨兵
资源配置:
主节点:8核32G内存
从节点:8核32G内存
哨兵节点:2核4G (3台)
配置优化:
# redis.conf
maxmemory 24gb
maxmemory–policy allkeys–lru
save 900 1
save 300 10
save 60 10000
# sentinel.conf
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel down–after–milliseconds mymaster 10000
sentinel failover–timeout mymaster 60000
sentinel parallel–syncs mymaster 1
监控告警:
@Component
public class SentinelMonitor {
@Scheduled(fixedDelay = 5000)
public void checkSentinelHealth() {
// 监控主从延迟
Long masterOffset = getMasterOffset();
List<Long> slaveOffsets = getSlaveOffsets();
for (Long slaveOffset : slaveOffsets) {
long delay = masterOffset – slaveOffset;
if (delay > 10000) { // 延迟超过10000条命令
alert("从节点同步延迟过高:" + delay);
}
}
// 监控哨兵状态
int activeSentinels = getActiveSentinels();
if (activeSentinels < 3) {
alert("哨兵节点异常,当前存活:" + activeSentinels);
}
}
}
4.2 业务场景2:大型社交平台
业务特征:
- 数据量:500GB+
- QPS:写入10万+,读取50万+
- 可用性要求:99.99%
- 业务模块:用户关系、Feed流、消息队列
架构方案:
部署架构:
9节点集群(6主3从)
节点分布:
可用区A:主节点x2 + 从节点x1
可用区B:主节点x2 + 从节点x1
可用区C:主节点x2 + 从节点x1
资源配置:
主节点:16核64G内存
从节点:16核64G内存
配置优化:
# redis.conf
cluster–enabled yes
cluster–config–file nodes.conf
cluster–node–timeout 5000
cluster–require–full–coverage no
cluster–migration–barrier 1
# 网络优化
tcp–backlog 511
timeout 0
tcp–keepalive 300
分片策略设计:
@Component
public class ShardingStrategy {
/**
* 用户关系数据分片
* 按用户ID哈希分布
*/
public String getUserRelationKey(long userId) {
return "{relation:" + (userId % 1000) + "}:user:" + userId;
}
/**
* Feed流数据分片
* 按时间维度分片,避免热点
*/
public String getFeedKey(long timestamp) {
// 按小时分片
String hourTag = "feed:" + (timestamp / 3600000);
return "{" + hourTag + "}:data";
}
/**
* 热点key处理
* 使用本地缓存+分布式锁
*/
@Cacheable(value = "hot:user", key = "#userId")
public User getUserWithLocalCache(long userId) {
String lockKey = "{lock:user:" + userId + "}";
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(1, 5, TimeUnit.SECONDS)) {
// 查Redis集群
return queryFromCluster(userId);
}
} finally {
lock.unlock();
}
return null;
}
}
4.3 业务场景3:金融支付系统
业务特征:
- 数据强一致性要求
- 不能容忍数据丢失
- 严格的审计要求
- 秒级故障恢复要求
架构方案:
部署架构:
3主3从集群 + 跨机房容灾
双机房部署:
主中心:3主 + 2从
备中心:1从 + 实时同步
数据一致性配置:
# redis.conf
min–replicas–to–write 2
min–replicas–max–lag 2
wait 2 1000 # 等待至少2个从节点确认
cluster–require–full–coverage yes
cluster–node–timeout 3000
强一致性实现:
@Service
public class PaymentService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 支付事务:要求强一致性
*/
public boolean processPayment(Payment payment) {
String key = "payment:" + payment.getOrderId();
String lockKey = "{lock:" + key + "}";
// 分布式锁
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock(5, TimeUnit.SECONDS);
// 开启Redis事务
redisTemplate.setEnableTransactionSupport(true);
SessionCallback<Object> callback = new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi();
try {
// 扣减余额
operations.opsForValue().decrement("balance:" + payment.getUserId(),
payment.getAmount());
// 记录支付流水
operations.opsForValue().set(key, payment.toString());
// 更新订单状态
operations.opsForValue().set("order:" + payment.getOrderId(),
"PAID");
// 执行事务并等待从节点确认
List<Object> results = operations.exec();
// WAIT命令确保数据同步到从节点
redisTemplate.execute((RedisCallback<Long>) connection ->
connection.waitReplicas(2, 1000));
return results;
} catch (Exception e) {
operations.discard();
throw e;
}
}
};
List<Object> results = redisTemplate.execute(callback);
return !results.contains(null);
} finally {
lock.unlock();
}
}
/**
* 数据核对机制
*/
@Scheduled(cron = "0 0/5 * * * ?")
public void verifyDataConsistency() {
// 对比主从数据
Long masterCount = getMasterDataCount();
Long slaveCount = getSlaveDataCount();
if (!masterCount.equals(slaveCount)) {
// 触发数据修复流程
triggerDataRepair();
// 发送告警
alert("Redis主从数据不一致!");
}
}
}
五、架构选型决策树
#mermaid-svg-RVrvxHi1hUogbuEX{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-RVrvxHi1hUogbuEX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RVrvxHi1hUogbuEX .error-icon{fill:#552222;}#mermaid-svg-RVrvxHi1hUogbuEX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RVrvxHi1hUogbuEX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RVrvxHi1hUogbuEX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RVrvxHi1hUogbuEX .marker.cross{stroke:#333333;}#mermaid-svg-RVrvxHi1hUogbuEX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RVrvxHi1hUogbuEX p{margin:0;}#mermaid-svg-RVrvxHi1hUogbuEX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-RVrvxHi1hUogbuEX .cluster-label text{fill:#333;}#mermaid-svg-RVrvxHi1hUogbuEX .cluster-label span{color:#333;}#mermaid-svg-RVrvxHi1hUogbuEX .cluster-label span p{background-color:transparent;}#mermaid-svg-RVrvxHi1hUogbuEX .label text,#mermaid-svg-RVrvxHi1hUogbuEX span{fill:#333;color:#333;}#mermaid-svg-RVrvxHi1hUogbuEX .node rect,#mermaid-svg-RVrvxHi1hUogbuEX .node circle,#mermaid-svg-RVrvxHi1hUogbuEX .node ellipse,#mermaid-svg-RVrvxHi1hUogbuEX .node polygon,#mermaid-svg-RVrvxHi1hUogbuEX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RVrvxHi1hUogbuEX .rough-node .label text,#mermaid-svg-RVrvxHi1hUogbuEX .node .label text,#mermaid-svg-RVrvxHi1hUogbuEX .image-shape .label,#mermaid-svg-RVrvxHi1hUogbuEX .icon-shape .label{text-anchor:middle;}#mermaid-svg-RVrvxHi1hUogbuEX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RVrvxHi1hUogbuEX .rough-node .label,#mermaid-svg-RVrvxHi1hUogbuEX .node .label,#mermaid-svg-RVrvxHi1hUogbuEX .image-shape .label,#mermaid-svg-RVrvxHi1hUogbuEX .icon-shape .label{text-align:center;}#mermaid-svg-RVrvxHi1hUogbuEX .node.clickable{cursor:pointer;}#mermaid-svg-RVrvxHi1hUogbuEX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RVrvxHi1hUogbuEX .arrowheadPath{fill:#333333;}#mermaid-svg-RVrvxHi1hUogbuEX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RVrvxHi1hUogbuEX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RVrvxHi1hUogbuEX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RVrvxHi1hUogbuEX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RVrvxHi1hUogbuEX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RVrvxHi1hUogbuEX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RVrvxHi1hUogbuEX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RVrvxHi1hUogbuEX .cluster text{fill:#333;}#mermaid-svg-RVrvxHi1hUogbuEX .cluster span{color:#333;}#mermaid-svg-RVrvxHi1hUogbuEX 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-RVrvxHi1hUogbuEX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RVrvxHi1hUogbuEX rect.text{fill:none;stroke-width:0;}#mermaid-svg-RVrvxHi1hUogbuEX .icon-shape,#mermaid-svg-RVrvxHi1hUogbuEX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RVrvxHi1hUogbuEX .icon-shape p,#mermaid-svg-RVrvxHi1hUogbuEX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RVrvxHi1hUogbuEX .icon-shape rect,#mermaid-svg-RVrvxHi1hUogbuEX .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RVrvxHi1hUogbuEX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RVrvxHi1hUogbuEX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RVrvxHi1hUogbuEX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
是
否
是
否
是
否
开始选型
数据量超过100GB?
写QPS超过5万?
Cluster集群
可用性要求99.95%以上?
哨兵模式
读写比例读远大于写?
主从复制+读写分离
确定架构
六、总结与最佳实践
6.1 各架构适用场景速查表
Redis 多业务场景架构选型与配置指南:
| 缓存加速 | < 10G | 主从 + 哨兵 (Master-Slave + Sentinel) | maxmemory-policy allkeys-lru maxmemory 8GB | • 监控:重点关注哨兵监控状态与主从复制延迟。 • 驱逐:确保 maxmemory 设置略低于物理内存,避免OOM。 |
| 会话存储 | < 50G | 哨兵模式 (Sentinel) | maxmemory-policy volatile-lru maxmemory 45GB | • 过期策略:应用层必须设置合理的过期时间(expire),防止内存泄露。 • 高可用:哨兵数量至少3个,部署在不同物理机。 |
| 计数统计 | < 100G | Cluster 集群 | cluster-node-timeout 5000 cluster-enabled yes | • 数据分片:使用 Hash Tag (例如 {user123}:count)确保相关key位于同一slot,支持多key操作。 • 扩缩容:规划好slot数量,预先分配节点。 |
| 支付交易 | < 500G | 强一致性集群 (Cluster + 强同步) | min-replicas-to-write 2 min-replicas-max-lag 5 appendonly yes appendfsync always | • 持久化:必须开启AOF且为always刷盘,配合appendfsync保证数据不丢。 • 一致性:至少同步写入2个从节点,牺牲部分性能换取强一致性。 • 备份:定期备份AOF文件到远程存储。 |
| 社交Feed | > 500G | 大规模集群 (Cluster + 异地多活) | cluster-migration-barrier 1 repl-backlog-size 512MB | • 动态扩容:采用预分片(预创建较多slot)或支持在线扩容的集群方案(如Redis Cluster或Codis)。 • 大Key处理:避免存储用户时间线大Key,建议拆分为多个小Key或使用其他组件。 • 网络:保证节点间网络低延迟,适当增大repl-backlog以应对网络抖动。 |
6.2 生产环境避坑指南
内存爆炸预防:
# 限制单个key大小
# redis.conf
proto-max-bulk-len 100mb
# 监控大key
redis-cli –bigkeys
网络分区防护:
// 应用层熔断
@HystrixCommand(fallbackMethod = "fallbackGetFromDB",
commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
})
public Object getFromRedis(String key) {
return redisTemplate.opsForValue().get(key);
}
版本升级策略:
# 集群滚动升级
1. 先升级从节点
2. 手动触发故障转移
3. 升级原主节点
4. 验证集群状态
# 升级前必须测试
redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000
容量规划公式:
// 内存计算公式
total_memory = (data_size * (1 + replication_factor) * 1.5) + buffer_size
// 数据量增长预测
growth_rate = 30% // 年增长率
capacity_plan = current_size * (1 + growth_rate) ^ years
// 连接数规划
max_clients = (max_connections_per_instance * node_count) * 0.8
6.3 性能调优清单
操作系统优化:
vm.overcommit_memory=1
net.core.somaxconn=1024
transparent_hugepage=disabled
Redis配置优化:
maxclients 20000
timeout 0
tcp–keepalive 300
lazyfree–lazy–eviction yes
lazyfree–lazy–expire yes
lazyfree–lazy–server–del yes
监控指标:
– 命中率 > 90%
– 内存碎片率 < 1.5
– 主从延迟 < 1s
– 拒绝连接数 = 0
– OOM killer次数 = 0
网硕互联帮助中心


评论前必须登录!
注册