redis:Redis Cluster与Sentinel模式深度剖析
为帮助阿里/字节跳动资深Java工程师深入理解RedisCluster与Sentinel模式,我将从架构、核心能力等多方面对比,结合项目案例、源码解析,并设置面试追问板块,全面剖析二者区别。
Redis Cluster与Sentinel模式深度剖析:从架构到工程实践
在分布式缓存领域,Redis的高可用方案一直是大厂面试的核心考点,也是生产环境架构设计的关键环节。本文将从架构设计、核心能力、数据流转、源码实现四个维度,结合阿里电商缓存集群与字节跳动短视频计数服务的实际案例,深度解析Redis Cluster与Sentinel模式的本质区别,并针对大厂高频追问提供工程化解决方案。
一、架构本质:分布式存储 vs 高可用监控
Redis Cluster与Sentinel模式的核心区别在于设计目标不同:Sentinel聚焦于「单点Redis的高可用」,通过监控实现主从故障转移;而Cluster则是「分布式存储+高可用」的完整解决方案,通过分片实现数据水平扩展。
1.1 Sentinel模式架构
Sentinel模式由「主从节点+Sentinel节点集群」组成,主节点负责写操作,从节点负责读操作和数据备份,Sentinel节点负责监控主从状态并执行故障转移。
注:Sentinel节点至少需要3个才能避免脑裂,通过Raft协议选举Leader执行故障转移。
1.2 Cluster模式架构
Cluster模式将数据分片存储在多个「主从节点组」中,每个主节点负责一部分槽位(共16384个槽),从节点作为主节点的备份。Cluster自身集成了高可用能力,无需额外Sentinel节点。
注:每个主节点通过Gossip协议与其他节点通信,维护集群元数据;当主节点故障时,其从节点会自动选举为新主节点。
二、核心能力对比:从数据分片到故障转移
| 对比维度 | Sentinel模式 | Cluster模式 |
|---|---|---|
| 数据分片 | 无分片能力,所有数据存储在单个主节点,受限于单机内存 | 支持槽位分片,16384个槽分布在多个主节点,实现水平扩展 |
| 高可用实现 | 依赖独立Sentinel集群,通过Raft协议选举Leader执行故障转移 | 内置高可用,主节点故障时从节点自动选举(基于Raft协议) |
| 客户端访问 | 客户端需通过Sentinel获取主节点地址,或直接连接主从(读扩散) | 客户端需支持槽位路由,或通过Proxy(如Codis)访问 |
| 脑裂防护 | 需配置min-replicas-to-write参数,确保主节点有足够从节点同步 | 通过quorum机制(超过半数主节点确认故障)避免脑裂 |
| 适用场景 | 数据量较小(GB级),需高可用但无需水平扩展的场景 | 数据量大(TB级),需水平扩展和高可用的分布式场景 |
三、数据流转时序图:从写操作到故障转移
3.1 Sentinel模式写操作与故障转移
3.2 Cluster模式写操作与故障转移
四、源码解析:核心机制的实现原理
4.1 Sentinel故障转移源码(Redis 6.2)
Sentinel的故障转移核心逻辑在sentinel.c中,主要包括三个阶段:故障检测、Leader选举、故障转移执行。
// 1. 故障检测:Sentinel定期向主节点发送PING命令
void sentinelCheckMasterFlags(sentinelRedisInstance *ri) {
if (ri->flags & SRI_MASTER) {
if (sentinelIsSubjectivelyDown(ri)) { // 主观下线(单个Sentinel检测到故障)
sentinelMarkAsSubjectivelyDown(ri);
sentinelAskMasterStateToOtherSentinels(ri); // 询问其他Sentinel
} else if (ri->flags & SRI_SDOWN) {
sentinelClearSubjectivelyDown(ri);
}
}
}
// 2. Leader选举:基于Raft协议的投票机制
int sentinelElectLeader(sentinelRedisInstance *ri) {
dictIterator *di;
dictEntry *de;
int votes = 0;
// 向其他Sentinel发送投票请求
di = dictGetIterator(sentinel.masters);
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *master = dictGetVal(de);
if (master == ri) continue;
if (sentinelSendVoteRequest(master, ri)) votes++;
}
dictReleaseIterator(di);
// 获得超过半数投票则成为Leader
return (votes > sentinelQuorum(ri)) ? 1 : 0;
}
// 3. 故障转移执行:选择从节点晋升为主节点
void sentinelFailoverStateMachine(sentinelRedisInstance *ri) {
switch(ri->failover_state) {
case SENTINEL_FAILOVER_STATE_SELECT_SLAVE:
ri->failover_slave = sentinelSelectSlave(ri); // 选择最优从节点
ri->failover_state = SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE;
break;
case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE:
redisAsyncCommand(ri->cc, sentinelFailoverSlaveOfNoOneReply, ri, "SLAVEOF NO ONE");
ri->failover_state = SENTINEL_FAILOVER_STATE_WAIT_PROMOTION;
break;
// 后续步骤:设置新主节点地址、通知其他从节点同步等
}
}
4.2 Cluster故障转移源码(Redis 6.2)
Cluster的故障转移逻辑在cluster.c中,核心是故障检测(Gossip协议)和从节点选举(Raft协议)。
// 1. Gossip协议:节点间交换集群状态
void clusterSendGossipPackets(void) {
listNode *ln;
listIter li;
listRewind(server.cluster->nodes, &li);
while((ln = listNext(&li)) != NULL) {
clusterNode *node = ln->value;
if (node->flags & (CLUSTER_NODE_MYSELF|CLUSTER_NODE_PFAIL)) continue;
clusterSendGossipPacket(node); // 发送包含节点状态的Gossip包
}
}
// 2. 故障检测:标记节点为客观下线
void clusterProcessGossipMessage(clusterNode *sender, clusterMsg *hdr) {
clusterNode *node = clusterLookupNode(hdr->data.gossip.nodeid);
if (node && hdr->data.gossip.pfail) {
clusterNodeAddFailureReport(node, sender); // 记录故障报告
if (clusterNodeIsMajorityPfail(node)) { // 超过半数主节点报告故障
clusterMarkNodeAsFail(node); // 标记为客观下线
}
}
}
// 3. 从节点选举:基于Raft协议
int clusterHandleSlaveElection(clusterNode *master) {
clusterNode *slave = server.cluster->myself;
if (!(slave->flags & CLUSTER_NODE_SLAVE) || slave->slaveof != master) return 0;
if (master->flags != CLUSTER_NODE_FAIL) return 0;
// 发起选举请求
clusterSendSlaveVoteRequests(master);
// 等待投票结果
if (slave->votes >= clusterQuorumForNode(master)) {
clusterPromoteSlave(); // 晋升为新主节点
return 1;
}
return 0;
}
五、工程实践案例
5.1 阿里电商:Sentinel模式在商品详情页缓存的应用
阿里电商商品详情页缓存初期采用Sentinel模式,原因是:商品详情数据量适中(单商品约1KB),总缓存量约50GB,单机128GB内存即可承载;且读请求量极大(每秒数十万QPS),通过「一主多从」实现读扩散。
优化点:为避免Sentinel故障转移期间的读抖动,客户端实现了「读多活」逻辑——同时连接多个从节点,当某个从节点不可用时自动切换到其他从节点;并通过「限流降级」机制,在主从切换期间将请求导流到DB。
5.2 字节跳动:Cluster模式在短视频计数服务的应用
字节跳动短视频的点赞、评论、播放量计数服务采用Cluster模式,原因是:单条视频计数数据量小(约100B),但总视频数超过10亿,总缓存量达TB级,需水平扩展;且计数操作需高并发写入(每秒百万级QPS)。
优化点:采用「预分片+动态扩缩容」策略,初始化时创建100个主节点槽位分布,后续根据数据量动态增加主节点;并通过「本地缓存+Redis Cluster」二级缓存,减少Redis访问压力。
六、大厂面试深度追问
追问1:Sentinel模式中,如何避免主从切换期间的数据丢失?
解决方案:需从「同步机制」和「故障转移策略」两方面优化:
-
配置同步参数:设置
min-replicas-to-write=2和min-replicas-max-lag=10,确保主节点至少有2个从节点的同步延迟不超过10秒,否则拒绝写操作,避免主节点故障时从节点数据过旧。 -
优先选择最新从节点:在Sentinel的
sentinelSelectSlave函数中,修改从节点选择策略,优先选择「复制偏移量最大」的从节点晋升为主节点,确保数据最新。 -
启用AOF持久化:主从节点均启用AOF持久化,并配置
appendfsync=everysec,确保数据写入磁盘的安全性。
追问2:Redis Cluster的槽位迁移过程中,如何保证数据一致性?
解决方案:Redis Cluster的槽位迁移采用「渐进式迁移」策略,核心步骤如下:
-
迁移准备:源主节点将槽位标记为「migrating」状态,目标主节点将槽位标记为「importing」状态,确保其他节点不会向该槽位写入数据。
-
数据迁移:源主节点通过
MIGRATE命令逐个迁移键值对到目标主节点,迁移期间源主节点仍可处理该槽位的读请求,写请求则返回「ASK」重定向到目标主节点。 -
槽位更新:迁移完成后,源主节点将槽位状态设为「none」,目标主节点将槽位状态设为「assigned」,并通过Gossip协议广播槽位变更信息。
-
客户端适配:客户端接收到「ASK」重定向时,临时向目标主节点发送请求;接收到「MOVED」重定向时,更新本地槽位路由表,永久指向目标主节点。
追问3:如何设计一个同时支持高可用和水平扩展的Redis架构?
解决方案:推荐采用「Redis Cluster+Proxy+监控平台」的架构,具体如下:
-
核心层:Redis Cluster:采用3主3从的Cluster集群,每个主节点负责5461个槽位,实现水平扩展和内置高可用。
-
接入层:Proxy(如Twemproxy/Codis):客户端通过Proxy访问Redis,Proxy负责槽位路由、负载均衡和故障屏蔽,降低客户端复杂度。
-
监控层:Prometheus+Grafana:监控集群槽位分布、节点状态、数据同步延迟等指标,设置告警阈值(如主从延迟超过5秒告警)。
-
运维层:自动化工具:开发自动化扩缩容工具,支持一键添加主从节点、迁移槽位;开发故障自愈工具,自动处理节点故障和槽位异常。
七、总结
这篇博客涵盖了多方面技术要点与实践案例。你若对某个部分想进一步细化,或有新的面试问题想补充,欢迎随时告知。
更多推荐



所有评论(0)