欢迎光临
我们一直在努力

Redis分片集群和分片逻辑,动态伸缩机制,故障转移

一、 分片集群架构概述与核心特征

主从复制与哨兵机制有效解决了单机Redis的高可用性与高并发读取瓶颈,但在面对海量数据存储与极高并发写入的场景时,单一主节点的内存容量上限与单点写入压力依然存在。为彻底突破这一架构局限,Redis引入了分片集群(Cluster)模式。关于分片集群的具体节点规划、配置文件调优与物理部署流程,请参阅专项的《Redis集群部署指南》。本文档将聚焦于分片集群的底层数据分片逻辑、动态伸缩机制与高可用故障转移原理。

分片集群的核心设计思想是数据分治。与主从架构中所有数据集中存储于单一主节点不同,分片集群在逻辑上由多个主节点共同构成,每个主节点仅负责存储全局数据集的一部分。集群中的每个主节点均可独立挂载一个或多个从节点,形成独立的复制链路以保障局部数据的高可用。节点之间通过内网互联,主节点与主节点之间持续通过ping命令进行心跳探测,以实时感知彼此的健康状态。对于应用层客户端而言,分片集群提供了统一的访问入口,客户端可向集群内的任意节点发起请求,若请求的键值未落在当前节点的管辖范围内,该节点会自动返回重定向指令,将客户端请求精准路由至目标数据节点。

二、 散列插槽机制与数据路由原理

分片集群实现数据分布式存储的核心载体是散列插槽(Hash Slot)。Redis集群将全局哈希空间均匀划分为16384个插槽(编号0至16383),并在集群初始化时将这部分插槽按比例分配给各个主节点。数据键并非直接绑定至物理节点,而是通过确定性算法映射至具体的插槽,再由插槽路由至对应的主节点。

Redis计算键值所属插槽的算法基于CRC16哈希函数。系统首先提取键的有效部分进行哈希运算:若键中包含花括号{},且括号内至少包含一个字符,则仅将花括号内部的内容视为有效部分;若键中不包含花括号,则整个键字符串均被视为有效部分。获取有效部分后,系统通过CRC16(有效部分) % 16384计算出最终的插槽编号。例如,在执行set a 1操作时,系统对键a进行CRC16运算并取模,得出插槽编号15495,该数据将被路由至负责该插槽的主节点;而在另一节点执行get num时,若计算出的插槽编号为2765,且该插槽归属另一主节点,则当前节点会向客户端返回MOVED重定向指令,引导其建立与目标节点的连接。 重定向后成功

为了保障关联数据的物理 locality(数据局部性),避免跨节点查询带来的网络延迟与事务限制,业务架构中通常采用哈希标签(Hash Tag)机制。通过将同类业务数据的键设计为相同的有效部分前缀(例如以{userId}或{typeId}包裹),可强制该类键的CRC16计算结果完全一致,从而确保其被固定分配至同一插槽与同一Redis实例中,为后续的业务关联查询与本地事务提供底层支撑。

#mermaid-svg-mArTYzRTo5D2XBGd{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-mArTYzRTo5D2XBGd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mArTYzRTo5D2XBGd .error-icon{fill:#552222;}#mermaid-svg-mArTYzRTo5D2XBGd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mArTYzRTo5D2XBGd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mArTYzRTo5D2XBGd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mArTYzRTo5D2XBGd .marker.cross{stroke:#333333;}#mermaid-svg-mArTYzRTo5D2XBGd svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mArTYzRTo5D2XBGd p{margin:0;}#mermaid-svg-mArTYzRTo5D2XBGd .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-mArTYzRTo5D2XBGd .cluster-label text{fill:#333;}#mermaid-svg-mArTYzRTo5D2XBGd .cluster-label span{color:#333;}#mermaid-svg-mArTYzRTo5D2XBGd .cluster-label span p{background-color:transparent;}#mermaid-svg-mArTYzRTo5D2XBGd .label text,#mermaid-svg-mArTYzRTo5D2XBGd span{fill:#333;color:#333;}#mermaid-svg-mArTYzRTo5D2XBGd .node rect,#mermaid-svg-mArTYzRTo5D2XBGd .node circle,#mermaid-svg-mArTYzRTo5D2XBGd .node ellipse,#mermaid-svg-mArTYzRTo5D2XBGd .node polygon,#mermaid-svg-mArTYzRTo5D2XBGd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mArTYzRTo5D2XBGd .rough-node .label text,#mermaid-svg-mArTYzRTo5D2XBGd .node .label text,#mermaid-svg-mArTYzRTo5D2XBGd .image-shape .label,#mermaid-svg-mArTYzRTo5D2XBGd .icon-shape .label{text-anchor:middle;}#mermaid-svg-mArTYzRTo5D2XBGd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mArTYzRTo5D2XBGd .rough-node .label,#mermaid-svg-mArTYzRTo5D2XBGd .node .label,#mermaid-svg-mArTYzRTo5D2XBGd .image-shape .label,#mermaid-svg-mArTYzRTo5D2XBGd .icon-shape .label{text-align:center;}#mermaid-svg-mArTYzRTo5D2XBGd .node.clickable{cursor:pointer;}#mermaid-svg-mArTYzRTo5D2XBGd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mArTYzRTo5D2XBGd .arrowheadPath{fill:#333333;}#mermaid-svg-mArTYzRTo5D2XBGd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mArTYzRTo5D2XBGd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mArTYzRTo5D2XBGd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mArTYzRTo5D2XBGd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mArTYzRTo5D2XBGd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mArTYzRTo5D2XBGd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mArTYzRTo5D2XBGd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mArTYzRTo5D2XBGd .cluster text{fill:#333;}#mermaid-svg-mArTYzRTo5D2XBGd .cluster span{color:#333;}#mermaid-svg-mArTYzRTo5D2XBGd 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-mArTYzRTo5D2XBGd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mArTYzRTo5D2XBGd rect.text{fill:none;stroke-width:0;}#mermaid-svg-mArTYzRTo5D2XBGd .icon-shape,#mermaid-svg-mArTYzRTo5D2XBGd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mArTYzRTo5D2XBGd .icon-shape p,#mermaid-svg-mArTYzRTo5D2XBGd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mArTYzRTo5D2XBGd .icon-shape .label rect,#mermaid-svg-mArTYzRTo5D2XBGd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mArTYzRTo5D2XBGd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mArTYzRTo5D2XBGd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mArTYzRTo5D2XBGd :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

客户端发起数据请求 Key

Key 是否包含大括号标签

提取大括号内部内容作为有效部分

将完整 Key 作为有效部分

CRC16 哈希运算

对 16384 取模计算 Slot 编号

查询当前节点路由表

Slot 是否归属当前节点

直接读写本地数据

返回 MOVED 指令重定向至目标节点

客户端更新路由缓存并重试

三、 集群动态伸缩与插槽迁移机制

分布式缓存系统必须具备水平扩展能力以应对业务流量的持续增长。分片集群通过动态插槽迁移机制实现了无缝的集群伸缩。当存储容量或写入吞吐量达到瓶颈时,管理员可通过redis-cli –cluster工具向现有集群注入新的主节点,并将部分原有插槽平滑迁移至新节点。

扩容操作首先需在独立服务器上初始化新的Redis实例(例如端口7004),并通过add-node命令将其注册至现有集群中。新节点加入后,默认以主节点身份运行,但其管辖的插槽数量为零,因此无法承载任何数据写入。为激活该节点,必须执行插槽迁移操作。管理员需预先确认目标数据键的插槽归属(例如num键位于插槽2765),随后发起reshard迁移指令,将源节点(例如7001)管辖的连续插槽区间(如0至3000)划拨至目标节点(7004)。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

插槽迁移过程由系统底层自动接管,采用渐进式数据搬迁策略。源节点在迁移期间会持续处理客户端请求,对于尚未迁移的插槽执行本地读写,对于已标记为迁移中或已迁移完成的插槽,则返回ASK或MOVED重定向指令。目标节点接收数据后逐步构建本地内存状态。整个迁移过程对业务层透明,仅在极短时间内可能产生微小的路由延迟,从而保障了集群在扩容期间的服务连续性。

#mermaid-svg-8JM75kl63OkikYDS{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-8JM75kl63OkikYDS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8JM75kl63OkikYDS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8JM75kl63OkikYDS .error-icon{fill:#552222;}#mermaid-svg-8JM75kl63OkikYDS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8JM75kl63OkikYDS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8JM75kl63OkikYDS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8JM75kl63OkikYDS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8JM75kl63OkikYDS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8JM75kl63OkikYDS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8JM75kl63OkikYDS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8JM75kl63OkikYDS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8JM75kl63OkikYDS .marker.cross{stroke:#333333;}#mermaid-svg-8JM75kl63OkikYDS svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8JM75kl63OkikYDS p{margin:0;}#mermaid-svg-8JM75kl63OkikYDS .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8JM75kl63OkikYDS .cluster-label text{fill:#333;}#mermaid-svg-8JM75kl63OkikYDS .cluster-label span{color:#333;}#mermaid-svg-8JM75kl63OkikYDS .cluster-label span p{background-color:transparent;}#mermaid-svg-8JM75kl63OkikYDS .label text,#mermaid-svg-8JM75kl63OkikYDS span{fill:#333;color:#333;}#mermaid-svg-8JM75kl63OkikYDS .node rect,#mermaid-svg-8JM75kl63OkikYDS .node circle,#mermaid-svg-8JM75kl63OkikYDS .node ellipse,#mermaid-svg-8JM75kl63OkikYDS .node polygon,#mermaid-svg-8JM75kl63OkikYDS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8JM75kl63OkikYDS .rough-node .label text,#mermaid-svg-8JM75kl63OkikYDS .node .label text,#mermaid-svg-8JM75kl63OkikYDS .image-shape .label,#mermaid-svg-8JM75kl63OkikYDS .icon-shape .label{text-anchor:middle;}#mermaid-svg-8JM75kl63OkikYDS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8JM75kl63OkikYDS .rough-node .label,#mermaid-svg-8JM75kl63OkikYDS .node .label,#mermaid-svg-8JM75kl63OkikYDS .image-shape .label,#mermaid-svg-8JM75kl63OkikYDS .icon-shape .label{text-align:center;}#mermaid-svg-8JM75kl63OkikYDS .node.clickable{cursor:pointer;}#mermaid-svg-8JM75kl63OkikYDS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8JM75kl63OkikYDS .arrowheadPath{fill:#333333;}#mermaid-svg-8JM75kl63OkikYDS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8JM75kl63OkikYDS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8JM75kl63OkikYDS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8JM75kl63OkikYDS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8JM75kl63OkikYDS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8JM75kl63OkikYDS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8JM75kl63OkikYDS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8JM75kl63OkikYDS .cluster text{fill:#333;}#mermaid-svg-8JM75kl63OkikYDS .cluster span{color:#333;}#mermaid-svg-8JM75kl63OkikYDS 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-8JM75kl63OkikYDS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8JM75kl63OkikYDS rect.text{fill:none;stroke-width:0;}#mermaid-svg-8JM75kl63OkikYDS .icon-shape,#mermaid-svg-8JM75kl63OkikYDS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8JM75kl63OkikYDS .icon-shape p,#mermaid-svg-8JM75kl63OkikYDS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8JM75kl63OkikYDS .icon-shape .label rect,#mermaid-svg-8JM75kl63OkikYDS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8JM75kl63OkikYDS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8JM75kl63OkikYDS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8JM75kl63OkikYDS :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

未完成

完成

启动新 Redis 实例 7004

执行 add-node 命令加入集群

新节点以 Master 身份注册, 初始 Slots 为 0

确定迁移范围: 从 7001 迁移 0~3000 插槽至 7004

源节点 7001 标记插槽为 migrating 状态

目标节点 7004 接收增量数据并构建本地状态

源节点逐步将存量数据同步至目标节点

迁移进度校验

更新集群路由表, 插槽正式归属 7004

集群扩容完成, 写入压力均匀分散

四、 分片集群故障转移机制

分片集群内置了去中心化的故障检测与自动恢复协议。当集群中的主节点因硬件故障或网络分区导致不可达时,系统将在不依赖外部哨兵组件的前提下,自主完成故障转移。

自动故障转移的触发链路遵循严格的超时判定逻辑。主节点下线后,首先与其他节点断开长连接,随后被集群内多数节点判定为疑似下线(PFAIL)。当集群中持有该主节点插槽映射信息的多数主节点均确认其下线状态后,该节点被正式标记为客观下线(FAIL)。此时,该主节点管辖的从节点将触发选举流程,通过Raft变种协议推选出一个数据最新、网络状态最优的从节点晋升为新主节点。新主节点接管原插槽的读写权限,集群路由表随之更新。当原故障节点修复并重新上线时,系统会自动将其身份降级为从节点,并使其同步至新主节点的数据状态,从而完成闭环恢复。

除了自动故障转移,分片集群还支持通过cluster failover命令执行手动故障切换。该机制允许管理员在指定的从节点上主动发起主从角色互换,常用于计划内的节点维护或性能调优。手动切换提供三种执行模式以适应不同的运维场景:

  • 默认模式(缺省)会严格遵循标准数据一致性校验流程;
  • force模式将跳过偏移量(offset)一致性校验,直接执行角色切换,适用于从节点数据延迟较低但需快速接管的场景;
  • takeover模式则最为激进,它将直接忽略数据一致性校验与源主节点的状态反馈,强制当前从节点立即晋升为主节点,适用于网络严重分裂或源主节点已彻底损坏的极端灾难恢复场景。

默认步骤:

其他节点(旧 Master/Slave)

Master 节点

Slave 节点

其他节点(旧 Master/Slave)

Master 节点

Slave 节点

#mermaid-svg-UgqQKSTv6MKzLbuA{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-UgqQKSTv6MKzLbuA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UgqQKSTv6MKzLbuA .error-icon{fill:#552222;}#mermaid-svg-UgqQKSTv6MKzLbuA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UgqQKSTv6MKzLbuA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UgqQKSTv6MKzLbuA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UgqQKSTv6MKzLbuA .marker.cross{stroke:#333333;}#mermaid-svg-UgqQKSTv6MKzLbuA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UgqQKSTv6MKzLbuA p{margin:0;}#mermaid-svg-UgqQKSTv6MKzLbuA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UgqQKSTv6MKzLbuA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-UgqQKSTv6MKzLbuA .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-UgqQKSTv6MKzLbuA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-UgqQKSTv6MKzLbuA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-UgqQKSTv6MKzLbuA .sequenceNumber{fill:white;}#mermaid-svg-UgqQKSTv6MKzLbuA #sequencenumber{fill:#333;}#mermaid-svg-UgqQKSTv6MKzLbuA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-UgqQKSTv6MKzLbuA .messageText{fill:#333;stroke:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UgqQKSTv6MKzLbuA .labelText,#mermaid-svg-UgqQKSTv6MKzLbuA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .loopText,#mermaid-svg-UgqQKSTv6MKzLbuA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .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-UgqQKSTv6MKzLbuA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-UgqQKSTv6MKzLbuA .noteText,#mermaid-svg-UgqQKSTv6MKzLbuA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-UgqQKSTv6MKzLbuA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UgqQKSTv6MKzLbuA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UgqQKSTv6MKzLbuA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-UgqQKSTv6MKzLbuA .actorPopupMenu{position:absolute;}#mermaid-svg-UgqQKSTv6MKzLbuA .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-UgqQKSTv6MKzLbuA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-UgqQKSTv6MKzLbuA .actor-man circle,#mermaid-svg-UgqQKSTv6MKzLbuA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-UgqQKSTv6MKzLbuA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

loop

[等待数据同步]

开始故障转移

通知 Master 拒绝任何客户端请求

返回当前数据 offset

检查 offset 是否与 Master 一致

广播故障转移结果(标记自己为 Master)

开始处理客户端读请求

三种执行模式:

#mermaid-svg-c8WMJj73XkV4HZrY{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-c8WMJj73XkV4HZrY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-c8WMJj73XkV4HZrY .error-icon{fill:#552222;}#mermaid-svg-c8WMJj73XkV4HZrY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-c8WMJj73XkV4HZrY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-c8WMJj73XkV4HZrY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-c8WMJj73XkV4HZrY .marker.cross{stroke:#333333;}#mermaid-svg-c8WMJj73XkV4HZrY svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-c8WMJj73XkV4HZrY p{margin:0;}#mermaid-svg-c8WMJj73XkV4HZrY .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-c8WMJj73XkV4HZrY .cluster-label text{fill:#333;}#mermaid-svg-c8WMJj73XkV4HZrY .cluster-label span{color:#333;}#mermaid-svg-c8WMJj73XkV4HZrY .cluster-label span p{background-color:transparent;}#mermaid-svg-c8WMJj73XkV4HZrY .label text,#mermaid-svg-c8WMJj73XkV4HZrY span{fill:#333;color:#333;}#mermaid-svg-c8WMJj73XkV4HZrY .node rect,#mermaid-svg-c8WMJj73XkV4HZrY .node circle,#mermaid-svg-c8WMJj73XkV4HZrY .node ellipse,#mermaid-svg-c8WMJj73XkV4HZrY .node polygon,#mermaid-svg-c8WMJj73XkV4HZrY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-c8WMJj73XkV4HZrY .rough-node .label text,#mermaid-svg-c8WMJj73XkV4HZrY .node .label text,#mermaid-svg-c8WMJj73XkV4HZrY .image-shape .label,#mermaid-svg-c8WMJj73XkV4HZrY .icon-shape .label{text-anchor:middle;}#mermaid-svg-c8WMJj73XkV4HZrY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-c8WMJj73XkV4HZrY .rough-node .label,#mermaid-svg-c8WMJj73XkV4HZrY .node .label,#mermaid-svg-c8WMJj73XkV4HZrY .image-shape .label,#mermaid-svg-c8WMJj73XkV4HZrY .icon-shape .label{text-align:center;}#mermaid-svg-c8WMJj73XkV4HZrY .node.clickable{cursor:pointer;}#mermaid-svg-c8WMJj73XkV4HZrY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-c8WMJj73XkV4HZrY .arrowheadPath{fill:#333333;}#mermaid-svg-c8WMJj73XkV4HZrY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-c8WMJj73XkV4HZrY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-c8WMJj73XkV4HZrY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c8WMJj73XkV4HZrY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-c8WMJj73XkV4HZrY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c8WMJj73XkV4HZrY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-c8WMJj73XkV4HZrY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-c8WMJj73XkV4HZrY .cluster text{fill:#333;}#mermaid-svg-c8WMJj73XkV4HZrY .cluster span{color:#333;}#mermaid-svg-c8WMJj73XkV4HZrY 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-c8WMJj73XkV4HZrY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-c8WMJj73XkV4HZrY rect.text{fill:none;stroke-width:0;}#mermaid-svg-c8WMJj73XkV4HZrY .icon-shape,#mermaid-svg-c8WMJj73XkV4HZrY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-c8WMJj73XkV4HZrY .icon-shape p,#mermaid-svg-c8WMJj73XkV4HZrY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-c8WMJj73XkV4HZrY .icon-shape .label rect,#mermaid-svg-c8WMJj73XkV4HZrY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-c8WMJj73XkV4HZrY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-c8WMJj73XkV4HZrY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-c8WMJj73XkV4HZrY :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

缺省 Default

Force 模式

Takeover 模式

管理员在指定 Slave 节点执行 cluster failover

选择执行模式

标准流程: 校验 Offset 一致性, 通知 Master 切换, 等待确认

跳过 Offset 一致性校验, 直接发起切换请求

忽略数据一致性校验与 Master 状态, 强制本地晋升

原 Master 降级为 Slave

原 Master 标记为 Slave

强制接管, 原 Master 待恢复后自动归队

当前 Slave 晋升为新 Master

更新集群路由表, 业务流量无缝切换

手动故障转移完成

五、 基于FastAPI的分片集群客户端集成

在分片集群架构下,数据节点的角色与物理拓扑结构会因自动故障转移与动态伸缩而频繁发生变化。与单机或哨兵模式不同,分片集群引入了哈希插槽(Hash Slot)的路由机制。因此,应用层客户端必须具备集群拓扑感知、自动重定向以及精细化的读写分离能力。在现代Python异步Web开发中,通常采用FastAPI框架结合异步Redis客户端(如redis-py的集群模块)来实现对分片集群的无缝集成。

5.1 拓扑感知与自动重定向机制

在分片集群中,客户端初始化时需传入集群内所有已知节点的地址列表作为引导节点(Startup Nodes)。底层驱动在首次建立连接时,会自动执行CLUSTER SLOTS命令拉取完整的集群路由表,并在本地构建插槽到节点的映射缓存。

当客户端发起读写请求时,底层驱动会首先根据键名计算CRC16哈希值并对16384取余,得出目标插槽编号,随后直接在本地缓存中查找该插槽对应的主节点并发起请求。然而,在集群发生动态伸缩或故障转移的瞬间,本地缓存可能与集群真实状态产生短暂的不一致。此时,若客户端将请求发送至了错误的节点,目标节点会返回MOVED或ASK重定向响应。企业级的异步Redis客户端能够自动捕获这些响应,在后台静默刷新本地路由缓存,并立即将请求重试转发至正确的目标节点。

5.2 读写分离策略与连接池管理

为适配企业级复杂的业务场景,异步Redis集群客户端支持灵活的读取策略配置。通过合理的架构设计,我们可在应用层动态选择路由策略:

  • PRIMARY:强制所有读写请求均路由至主节点,适用于对数据实时一致性要求极高的核心交易链路。
  • PREFER_PRIMARY:优先从主节点读取数据,仅在主节点不可用时降级至从节点,适用于读多写少且对延迟极度敏感的场景。
  • REPLICA:强制所有读请求路由至从节点,彻底释放主节点的CPU与网络资源,适用于海量数据查询与离线报表统计。
  • PREFER_REPLICA:优先从从节点读取数据,当所有从节点均发生故障时才回退至主节点,这是兼顾高并发读取与系统整体高可用性的最优企业级实践。
  • 在代码实现层面,必须警惕一个常见的架构陷阱:若在每次HTTP请求的依赖注入中重新实例化RedisCluster对象,将导致底层TCP连接池无法复用,进而引发严重的连接泄漏与性能崩塌。正确的企业级实践是利用全局字典缓存不同策略的客户端实例,确保连接池的生命周期与应用进程保持一致。

    from enum import Enum
    import redis.asyncio as aioredis
    from redis.asyncio.cluster import RedisCluster, ClusterNode
    from fastapi import FastAPI, Depends

    app = FastAPI(title="Redis Cluster Integration")

    class ReadPolicy(str, Enum):
    PRIMARY = "PRIMARY"
    PREFER_PRIMARY = "PREFER_PRIMARY"
    REPLICA = "REPLICA"
    PREFER_REPLICA = "PREFER_REPLICA"

    # 分片集群引导节点地址配置
    CLUSTER_NODES = [
    ClusterNode("192.168.150.101", 7001),
    ClusterNode("192.168.150.101", 7002),
    ClusterNode("192.168.150.101", 7003)
    ]

    # 全局维护不同策略的连接池,避免每次请求重复实例化导致连接泄漏
    _cluster_clients: dict[ReadPolicy, RedisCluster] = {}

    def get_cluster_client(policy: ReadPolicy = ReadPolicy.PREFER_REPLICA) > RedisCluster:
    """
    根据读写分离策略获取或初始化 Redis Cluster 异步客户端连接池
    """

    if policy not in _cluster_clients:
    # 针对 REPLICA 和 PREFER_REPLICA 策略,开启底层的从节点读取支持
    enable_replica_read = policy in [ReadPolicy.REPLICA, ReadPolicy.PREFER_REPLICA]

    client = RedisCluster(
    startup_nodes=CLUSTER_NODES,
    decode_responses=True,
    read_from_replicas=enable_replica_read,
    # 开启自动重定向与拓扑刷新机制,遭遇 MOVED/ASK 时自动重试
    reinitialize_steps=10,
    socket_timeout=0.5
    )
    _cluster_clients[policy] = client

    return _cluster_clients[policy]

    @app.get("/cache/{key}")
    async def read_cache(key: str, client: RedisCluster = Depends(get_cluster_client)):
    """
    基于集群拓扑自动路由的异步缓存读取
    底层驱动自动计算 key 的哈希值并定位 Slot,若遇拓扑变更则自动重试
    """

    value = await client.get(key)
    return {"key": key, "value": value}

    @app.post("/cache/{key}/{value}")
    async def write_cache(key: str, value: str, client: RedisCluster = Depends(lambda: get_cluster_client(ReadPolicy.PRIMARY))):
    """
    写操作强制路由至对应 Slot 的主节点
    通过 lambda 表达式显式覆盖默认策略,杜绝向从节点写入数据
    """

    await client.set(key, value)
    return {"status": "success", "key": key}

    @app.on_event("shutdown")
    async def shutdown_event():
    """
    应用关闭时优雅释放所有集群连接池资源
    """

    for client in _cluster_clients.values():
    await client.close()

    5.3 客户端与分片集群交互时序推演

    为了更直观地理解上述代码在复杂网络环境下的真实流转,以下时序图详细推演了当业务发起读取请求,且目标插槽恰好发生迁移(触发MOVED重定向)时,底层驱动的完整自动化交互链路。

    Node B (新主节点/正确节点)

    Node A (旧主节点/错误节点)

    RedisCluster 客户端驱动

    FastAPI 业务路由

    Node B (新主节点/正确节点)

    Node A (旧主节点/错误节点)

    RedisCluster 客户端驱动

    FastAPI 业务路由

    #mermaid-svg-BpxZFbDznJZsYWNF{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-BpxZFbDznJZsYWNF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BpxZFbDznJZsYWNF .error-icon{fill:#552222;}#mermaid-svg-BpxZFbDznJZsYWNF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BpxZFbDznJZsYWNF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BpxZFbDznJZsYWNF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BpxZFbDznJZsYWNF .marker.cross{stroke:#333333;}#mermaid-svg-BpxZFbDznJZsYWNF svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BpxZFbDznJZsYWNF p{margin:0;}#mermaid-svg-BpxZFbDznJZsYWNF .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BpxZFbDznJZsYWNF text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-BpxZFbDznJZsYWNF .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-BpxZFbDznJZsYWNF .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-BpxZFbDznJZsYWNF .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-BpxZFbDznJZsYWNF .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-BpxZFbDznJZsYWNF #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-BpxZFbDznJZsYWNF .sequenceNumber{fill:white;}#mermaid-svg-BpxZFbDznJZsYWNF #sequencenumber{fill:#333;}#mermaid-svg-BpxZFbDznJZsYWNF #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-BpxZFbDznJZsYWNF .messageText{fill:#333;stroke:none;}#mermaid-svg-BpxZFbDznJZsYWNF .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BpxZFbDznJZsYWNF .labelText,#mermaid-svg-BpxZFbDznJZsYWNF .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-BpxZFbDznJZsYWNF .loopText,#mermaid-svg-BpxZFbDznJZsYWNF .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-BpxZFbDznJZsYWNF .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-BpxZFbDznJZsYWNF .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-BpxZFbDznJZsYWNF .noteText,#mermaid-svg-BpxZFbDznJZsYWNF .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-BpxZFbDznJZsYWNF .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BpxZFbDznJZsYWNF .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BpxZFbDznJZsYWNF .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-BpxZFbDznJZsYWNF .actorPopupMenu{position:absolute;}#mermaid-svg-BpxZFbDznJZsYWNF .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-BpxZFbDznJZsYWNF .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-BpxZFbDznJZsYWNF .actor-man circle,#mermaid-svg-BpxZFbDznJZsYWNF line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-BpxZFbDznJZsYWNF :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    alt

    [拓扑已变更 (Slot 8000 已迁移至 Node B)]

    [拓扑未变更 (正常路由)]

    1. 发起 GET 请求 (Key="user:1001")

    2. 计算 CRC16("user:1001") % 16384 = Slot 8000

    3. 查询本地路由缓存,认为 Slot 8000 属于 Node A

    4. 发送 GET user:1001 命令

    5. 返回 MOVED 8000 192.168.150.101:7002 (重定向响应)

    6. 捕获 MOVED,后台异步刷新本地路由缓存表

    7. 自动重试,发送 GET user:1001 命令至 Node B

    8. 返回正常业务数据

    9. 返回解析后的业务数据 (对业务层完全透明)

    5. 返回正常业务数据

    6. 返回解析后的业务数据

    六、 知识点总结

  • 分片集群的核心价值:通过多主节点架构解决海量数据存储与高并发写入瓶颈,每个主节点独立管辖部分数据并挂载从节点,主节点间通过心跳协议维持集群拓扑一致性。
  • 散列插槽路由机制:集群固定划分16384个插槽,数据键通过CRC16(有效部分) % 16384计算归属插槽。利用{}哈希标签可强制关联数据落入同一插槽与物理实例。
  • 集群动态伸缩原理:通过redis-cli –cluster工具注入新节点并执行渐进式插槽迁移。迁移期间采用migrating状态标记与MOVED/ASK重定向指令,保障业务连续性。
  • 故障转移双模机制:自动转移基于多数派共识协议完成主从角色切换;手动转移通过cluster failover命令支持缺省、force(跳过偏移量校验)与takeover(强制接管忽略一致性)三种运维模式。
  • 客户端集成规范:现代异步框架(如FastAPI)需集成支持集群协议的异步驱动,配置已知节点列表以自动拉取路由表,并通过读取策略参数(PRIMARY/PREFER_REPLICA等)实现读写分离的路由控制。
  • 赞(0)
    未经允许不得转载:171主机测评 » Redis分片集群和分片逻辑,动态伸缩机制,故障转移
    分享到: 更多 (0)

    评论 抢沙发

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