面试必备之redis主从,哨兵,集群之间的权力的游戏
mhr18 2025-03-19 14:25 41 浏览 0 评论
Redis 主从、哨兵、集群:从单机到分布式,一场数据的“权力游戏”
大家好,今天我们要聊的是 Redis 的主从复制、哨兵机制和集群模式。如果你觉得这些名词听起来像是《权力的游戏》里的某个家族或者某种神秘组织,那你可没错!在这场数据的“权力游戏”中,Redis 的角色从单机小兵逐渐成长为分布式大军,每一步都充满了挑战与智慧。准备好了吗?让我们一起踏上这场 Redis 的奇幻之旅!
第一章:单机 Redis 的“孤独王者”
在故事的开始,Redis 是一个孤独的王者,它独自守护着数据的城堡。所有的读写请求都由它一个人处理,简单、直接、高效。就像《权力的游戏》里的夜王,单枪匹马,无人能敌。
但问题来了,夜王再强,也会有疲惫的时候。如果 Redis 的服务器宕机了,数据就会丢失,服务就会中断。这时候,我们需要一个“备胎”,一个可以随时接班的副手。于是,Redis 的主从复制登场了。
第二章:主从复制——Redis 的“兄弟连”
主从复制(Master-Slave Replication)是 Redis 的第一个“权力扩张”。主节点(Master)负责写操作,而从节点(Slave)则负责复制主节点的数据,并提供读服务。这样一来,即使主节点宕机,从节点也能顶上,确保服务不中断。
主从复制的原理
- 同步(SYNC):当从节点第一次连接主节点时,主节点会执行一次全量同步,将自己的所有数据发送给从节点。
- 命令传播(Command Propagation):之后,主节点每执行一个写命令,都会将这个命令发送给从节点,从节点执行相同的命令,保持数据一致。
主从复制的优缺点
优点:
- 读写分离:主节点负责写,从节点负责读,分担了主节点的压力。
- 数据备份:从节点是主节点的完整副本,即使主节点宕机,数据也不会丢失。
缺点:
- 单点故障:如果主节点宕机,虽然从节点有数据,但无法自动切换为主节点,需要手动干预。
- 写瓶颈:所有的写操作仍然集中在主节点,主节点仍然是系统的瓶颈。
思考题
如果你有一个高并发的写场景,主从复制能否满足需求?如果不能,你会怎么解决?
第三章:哨兵机制——Redis 的“守护者”
既然主从复制无法自动处理主节点的故障,那我们是不是需要一个“守护者”来监控主节点的状态,并在主节点宕机时自动进行故障转移?没错,这就是 Redis 的哨兵机制(Sentinel)。
哨兵机制的原理
哨兵是一个独立的进程,它会监控 Redis 主从节点的健康状态。当主节点宕机时,哨兵会自动将一个从节点提升为新的主节点,并通知其他从节点切换主节点。
- 哨兵集群启动过程中做的事情(哨兵是主服务器的客户端)
- 初始化服务器
- 使用Sentinel专用代码
- 初始化Sentinel状态
- 创建连向主服务器的网络连接
命令连接,用于向主服务器发送命令,并接受命令。
订阅连接,专门用于订阅主服务器的_sentinel_:hello频道
- 获取主服务器的信息
哨兵默认会以10s一次的频率,发送命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的当前信息。
监控主服务器
# +monitor master learnSentinelMaster 192.168.17.101 6379 quorum 2
通过分析主服务器返回的信息,可以获取到两方面的信息
主服务器本身的信息 和 从服务器的信息
获取到从服务器信息之后,哨兵会更新保存主服务器实例结构的slaves字典
- 获取从服务器的信息
当哨兵发现主服务器有新的从服务器出现时,哨兵会为这个新的从服务器创建相应的实例结构之外,还会创建到从服务器的命令连接和订阅连接。
在创建命令连接之后,会发送INFO命令获取信息。通过从服务器回复的信息中,可以获得以下内容
- 从服务器的运行ID run_id
- 从服务器的角色 role
- 从服务器的IP地址 master_host,以及主服务器的端口号master_port
- 从服务器的连接状态 matser_link_status
- 从服务器的优先级 salve_pripority
- 从服务器的复制偏移量 slave_repl_offest
获取到这些信息之后,会对之前创建的从服务器实例结构进行更新新的从服务器创建相应的实例结构之外,还会创建到从服务器的命令连接和订阅连接。
- 获取其他sentinel 信息
在获取其他哨兵的信息之前,先要知道向主服务器和从服务器发送信息和接收来自主服务器和从服务器的频道信息
PUBLISH __sentinel__:hello ",,,,,,,"
- 接受来自主服务器和从服务器的信息
当Sentinel与一个主服务器或者从服务器建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送命令:
SUBSCRIBE __sentinel__:hello 复制代码
表示哨兵订阅__sentinel__:hello这个频道,接收这个频道的消息。
其他哨兵可以通过接收这个频道的消息来发现其他哨兵的存在
- 发现哨兵
通过接收__sentinel__:hello频道的消息可以发现其他哨兵的存在。当哨兵接收到一条来自__sentinel__:hello频道的消息时,会出现下方
- 判断该消息是否是自己发送的,是则忽略这条消息
- 消息不是自己发送时,说明有新的哨兵
- 查看自己是否存有该哨兵的信息,有则更新该哨兵的信息
- 没有则创建一个新的哨兵实例结构,并保存到sentinels字典中
- 创建连向其他哨兵的命令链接
当Sentinel通过频道信息发现一个新的Sentinel时,不仅会在自身的sentinels字典中为新Sentinel创建实例结构,还会创建一个连向新Sentinel的命令连接,同时新的Sentinel也会创建一个连向这个Sentinel的命令连接,最终多个Sentinel将形成一个互相连接的网络。
- 主服务器下线
- 检测主观下线状态
- 检测客观下线状态
- 选举领头哨兵
- 故障转移
- 检测主观下线
在默认情况下,Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(主,从,其他Sentinel)发送 PING命令 ,通过判断返回的内容来判断是否在线,命令分为有效回复和无效回复两种。
- 有效回复
- +PING
- -LOADING
- -MASTERDOWN
- 无效回复
- 除有效回复以外的内容
- 指定时间内没有回复
配置文件中的down-after-milliseconds参数可以设置指定时间,在这个时间段内没有收到回复则判定该服务器处于主观下线状态。
sentinel down-after-milliseconds learnSentinelMaster 5000
- 检测客观下线
当一个哨兵将一个主服务器判断为主观下线之后,会向其他监视该主服务器的哨兵进行询问,当有足够数量的哨兵判定主服务器下线时,会执行故障转移操作 。
注:这里不对哨兵之间互相发送的消息进行说明
在配置中可以决定判定主服务器进入客观下线状态所需要的服务器数量,下方配置的最后一个参数就是所需的哨兵数量,这里填写的是2
sentinel monitor learnSentinelMaster 192.168.17.101 6379 2
- 选举领头哨兵
当主服务器被判定为客观下线之后,各个哨兵服务器将会选举出一个领头哨兵,有这个领头哨兵对下线服务器进行故障转移操作,选举领头哨兵的规则如下:
- 所有在线的Sentinel都有被选为领头Sentinel的资格;
- 每次进行选举之后,不论选举是否成功,所有Sentinel的配置纪元都会自增一次;
- 在一个配置纪元里,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里就不会再更改;
- 每个发现主服务器进入客观下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel;
- 当一个Sentinel向另一个Sentinel发送请求命令,并且命令中的runid不是*而是运行id时,这表示源Sentinel要求目标Sentinel将前者设置为后者的局部领头Sentinel。
- 设置局部领头Sentinel的原则是先到先得,之后所有的设置要求都会被拒绝;
- 目标Sentinel在收到命令后,会返回一条回复,回复中的leader_runid参数和leader_epoch参数分别记录了目标Sentinel的局部领头Sentinel的运行ID和配置纪元;
- 源Sentinel在收到回复后,会检查配置纪元与自己是否相等,如果相同,且leader_runid与自己相同,那么表示自己成为了目标的局部领头;
- 如果有某个Sentinel被半数以上的Sentinel设置成了局部领头Sentinel,那么它成为领头Sentinel;
- 因为领头的产生需要半数哨兵的支持,并且每个哨兵在每个配置纪元只能设置一次局部领头Sentinel,所以在一个配置纪元里面,只会出现一个领头Sentinel;
- 如果在给定时限内没有选出领头Sentinel,那么各个Sentinel将在一段时间之后再次进行选举,直到选出来。
- 故障迁移
在选举出领头哨兵之后,领头哨兵需要执行故障转移操作,操作主要分为三个步骤
- 选出新的主服务器
- 修改从服务器的复制目标
- 将旧的主服务器变为从服务器
- 选举新的主服务器
此时,领头哨兵需要选出新的主服务器,然后向新的主服务器发送SLAVEOF no one命令,将这个从服务器转换为主服务器。
选择过程会过滤掉不符合要求的服务器:
- 处于下线或者断线状态的从服务器
- 最近5秒内没有回复过领头哨兵的INFO信息的从服务器
- 与已下线主服务器连接断开超过(down-after-milliseconds * 10)毫秒的从服务器。(与主服务器客观下线时间进行比较)
新的主服务器只选择通过上面的测试,并在上面的标准基础上排序:
- Slave通过Redis实例的redis.conf文件配置的slave-priority排序。优先级越低越被优先考虑。
- 如果优先级相同,检查slave的复制偏移量,并选择接收更多数据的slave。
- 如果多个slave有相同的优先级和同样的处理数据过程,就会执行一个更进一步的验证,选择一个有较短run ID的slave。run ID 对于 slave没太大用,但是非常有助于选择slave的过程,而不是随机选择slave。
- 修改从服务器的复制目标
当新的主服务器出现之后,领头哨兵会向其他从服务器发送slaveof 命令去复制新的主服务器。
下方记录了领头哨兵向从服务器发送 SALVEOF命令去复制新的主服务器
- 将旧的主服务器变为从服务器
这时候如果下线的主服务器重启上线了怎么办?这也是故障转移要做的最后一步,将已下线的主服务器设置为新的主服务器的从服务器。当下线的主服务器重新上线时,哨兵就会向它发送SLAVEOF命令,让他成为新的主服务器的从服务器。
哨兵机制的优缺点
优点:
- 自动故障转移:哨兵可以自动检测主节点故障并进行切换,减少了人工干预。
- 高可用性:通过多个哨兵实例,可以实现高可用的监控系统。
缺点:
- 配置复杂:哨兵的配置相对复杂,尤其是在大规模集群中。
- 脑裂问题:在网络分区的情况下,可能会出现多个主节点,导致数据不一致。
思考题
如果哨兵机制在网络分区的情况下出现了脑裂问题,你会如何解决?有没有其他分布式系统遇到过类似的问题?
脑裂问题描述
脑裂问题是指在分布式系统中,由于网络分区(Network Partition)或节点故障,导致集群中的节点无法正常通信,进而出现多个节点同时认为自己是主节点(Master)的情况。这种情况下,系统会出现多个“大脑”,每个“大脑”都认为自己是对的,导致数据不一致和服务混乱。
在 Redis 哨兵机制中,脑裂问题通常发生在以下场景:
- 网络分区:主节点和部分哨兵节点之间的网络断开,导致哨兵们无法感知主节点的状态。
- 主节点假死:主节点由于负载过高或资源耗尽,无法及时响应哨兵的心跳检测,哨兵误认为主节点宕机。
- 哨兵误判:哨兵之间的通信出现问题,导致部分哨兵认为主节点宕机,而另一部分哨兵认为主节点仍然存活。
当这些情况发生时,哨兵可能会选举出一个新的主节点,而原来的主节点仍然在运行。于是,系统中就出现了两个主节点,每个主节点都可能接收写请求,导致数据不一致。
脑裂问题的具体场景
让我们通过一个具体的场景来理解脑裂问题:
- 初始状态:一个 Redis 集群由一个主节点(Master)和两个从节点(Slave)组成,同时有三个哨兵(Sentinel)监控主节点的状态。
- 网络分区:突然,主节点和其中一个哨兵之间的网络断开,导致这个哨兵无法检测到主节点的心跳。
- 哨兵选举:这个哨兵认为主节点宕机,于是发起故障转移,选举出一个新的主节点。
- 双主节点:此时,原来的主节点仍然在运行(因为它只是网络不通,并没有真正宕机),而新的主节点也被选举出来。于是,系统中出现了两个主节点。
- 数据不一致:客户端可能会将写请求发送到不同的主节点,导致两个主节点的数据不一致。
脑裂问题的危害
脑裂问题带来的危害是显而易见的:
- 数据不一致:两个主节点同时接收写请求,可能导致相同的数据被修改为不同的值。
- 服务混乱:客户端可能会连接到不同的主节点,导致读取到不一致的数据。
- 恢复困难:当网络分区恢复后,系统需要手动或自动解决数据冲突,这可能非常复杂且耗时。
如何解决脑裂问题?
既然脑裂问题如此危险,我们该如何应对呢?以下是几种常见的解决方案:
1.增加哨兵数量
哨兵的数量越多,误判的概率就越低。通常建议至少部署 3 个哨兵实例,以确保在部分节点故障时,哨兵仍然能够做出正确的决策。
2.设置合理的超时时间
哨兵通过心跳检测来判断主节点是否存活。如果超时时间设置得太短,哨兵可能会频繁误判;如果设置得太长,故障转移的延迟会很高。通常建议根据网络环境和系统负载调整超时时间。
3.使用min-slaves-to-write参数
Redis 提供了 min-slaves-to-write 参数,可以设置主节点在写入数据时至少需要多少个从节点处于正常状态。如果从节点数量不足,主节点会拒绝写请求,从而避免数据不一致。
4.手动干预
当脑裂问题发生时,可以通过手动干预来解决。例如,强制关闭其中一个主节点,或者使用 CLUSTER FAILOVER 命令手动切换主节点。
5.使用 Redis 集群
Redis 集群(Cluster)通过分片(Sharding)和 Gossip 协议来避免脑裂问题。每个分片只有一个主节点,集群会自动检测节点状态并进行故障转移。
6.集群的一致性hash
常规hash
Redis服务器变动时,所有缓存的位置都会发生改变 还会造成数据不均匀的情况
一致性Hash算法原理
一致性Hash算法也是使用取模的方法,不过,上述的取模方法是对服务器的数量进行取模,而一致性的Hash算法是对2的32方取模。即,一致性Hash算法将整个Hash空间组织成一个虚拟的圆环,Hash函数的值空间为0 ~ 2^32 - 1(一个32位无符号整型),整个哈希环如下:
·
一致性Hash算法的容错性和可扩展性
现在,假设我们的Node C宕机了,我们从图中可以看到,A、B不会受到影响,只有Object C对象被重新定位到Node A。所以我们发现,在一致性Hash算法中,如果一台服务器不可用,受影响的数据仅仅是此服务器到其环空间前一台服务器之间的数据(这里为Node C到Node B之间的数据),其他不会受到影响。
数据倾斜问题
在一致性Hash算法服务节点太少的情况下,容易因为节点分布不均匀面造成数据倾斜(被缓存的对象大部分缓存在某一台服务器上)问题
数据定位算法不变,只需要增加一步:虚拟节点到实际点的映射。
所以加入虚拟节点之后,即使在服务节点很少的情况下,也能做到数据的均匀分布
脑裂问题与其他分布式系统的对比
脑裂问题并不是 Redis 独有的,许多分布式系统都会面临类似的问题。例如:
- ZooKeeper:ZooKeeper 通过 Zab 协议和 Leader 选举机制来避免脑裂问题。
- Kafka:Kafka 通过 ISR(In-Sync Replicas)机制来确保数据的一致性。
- Cassandra:Cassandra 通过 Gossip 协议和一致性哈希来避免脑裂问题。
这些系统的设计思想与 Redis 哨兵机制有异曲同工之妙,但它们在解决脑裂问题时采用了不同的策略。例如,ZooKeeper 通过严格的 Leader 选举机制来确保只有一个主节点,而 Kafka 则通过 ISR 机制来确保数据的强一致性。
第四章:Redis 集群——分布式时代的“铁王座”
随着数据量的增长和业务的扩展,单机 Redis 和主从复制已经无法满足需求。我们需要一个分布式的解决方案,这就是 Redis 集群(Cluster)。
Redis 集群的原理
Redis 集群采用分片(Sharding)的方式,将数据分散到多个节点上。每个节点负责一部分数据,节点之间通过 Gossip 协议进行通信,确保数据的一致性。
Redis 集群的优缺点
优点:
- 高扩展性:可以通过增加节点来扩展集群的容量和性能。
- 高可用性:每个分片都有主从复制,确保数据的高可用性。
缺点:
- 复杂性:集群的配置和管理比单机和主从复制复杂得多。
- 数据迁移:在节点增加或减少时,需要进行数据迁移,可能会影响性能。
思考题
如果你有一个超大规模的数据集,Redis 集群的分片策略是否足够?你会如何优化数据分布和查询性能?
第五章:Redis 与其他分布式系统的“联姻”
Redis 的主从、哨兵和集群机制并不是孤立的,它们与许多其他分布式系统的设计思想有着异曲同工之妙。比如:
- ZooKeeper:ZooKeeper 的 Leader 选举机制与 Redis 哨兵的故障转移非常相似。
- Kafka:Kafka 的分区(Partition)机制与 Redis 集群的分片策略有异曲同工之妙。
- Cassandra:Cassandra 的 Gossip 协议与 Redis 集群的节点通信机制如出一辙。
思考题
你能想到其他分布式系统中与 Redis 类似的设计吗?它们之间有哪些异同点?
结语:Redis 的“权力游戏”永不停歇
从单机到主从,从哨兵到集群,Redis 在这场数据的“权力游戏”中不断进化。每一次的升级都是为了应对更大的挑战,解决更复杂的问题。而作为开发者的我们,也需要不断学习和思考,才能在这场游戏中立于不败之地。
所以,下次当你使用 Redis 时,不妨想想它背后的这些机制,它们不仅仅是技术的实现,更是分布式系统设计思想的体现。希望这篇文章能为你带来一些新的思考和启发,也欢迎你在评论区分享你的见解和经验。
最后,愿 Redis 的“权力游戏”永不停歇,愿你的代码永远没有 Bug!
相关推荐
- Redis合集-使用benchmark性能测试
-
采用开源Redis的redis-benchmark工具进行压测,它是Redis官方的性能测试工具,可以有效地测试Redis服务的性能。本次测试使用Redis官方最新的代码进行编译,详情请参见Redis...
- Java简历总被已读不回?面试挂到怀疑人生?这几点你可能真没做好
-
最近看了几十份简历,发现大部分人不是技术差,而是不会“卖自己”——一、简历死穴:你写的不是经验,是岗位说明书!反面教材:ד使用SpringBoot开发项目”ד负责用户模块功能实现”救命写法:...
- redission YYDS(redission官网)
-
每天分享一个架构知识Redission是一个基于Redis的分布式Java锁框架,它提供了各种锁实现,包括可重入锁、公平锁、读写锁等。使用Redission可以方便地实现分布式锁。red...
- 从数据库行锁到分布式事务:电商库存防超卖的九重劫难与破局之道
-
2023年6月18日我们维护的电商平台在零点刚过3秒就遭遇了严重事故。监控大屏显示某爆款手机SKU_IPHONE13_PRO_MAX在库存仅剩500台时,订单系统却产生了1200笔有效订单。事故复盘发...
- SpringBoot系列——实战11:接口幂等性的形而上思...
-
欢迎关注、点赞、收藏。幂等性不仅是一种技术需求,更是数字文明对确定性追求的体现。在充满不确定性的网络世界中,它为我们建立起可依赖的存在秩序,这或许正是技术哲学最深刻的价值所在。幂等性的本质困境在支付系...
- 如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享
-
如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享在高流量场景下。首先,我需要回忆一下常见的优化策略,比如负载均衡、缓存、数据库优化、微服务拆分这些。不过,可能还需要考虑用户的具体情况,比...
- Java面试题: 项目开发中的有哪些成长?该如何回答
-
在Java面试中,当被问到“项目中的成长点”时,面试官不仅想了解你的技术能力,更希望看到你的问题解决能力、学习迭代意识以及对项目的深度思考。以下是回答的策略和示例,帮助你清晰、有说服力地展示成长点:一...
- 互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?
-
你有没有遇到过这样的情况?在电商大促时,系统上线了抢券活动,结果活动刚一开始,服务器就不堪重负,出现超卖、系统崩溃等问题。又或者用户疯狂点击抢券按钮,最后却被告知无券可抢,体验极差。作为互联网大厂的后...
- 每日一题 |10W QPS高并发限流方案设计(含真实代码)
-
面试场景还原面试官:“如果系统要承载10WQPS的高并发流量,你会如何设计限流方案?”你:“(稳住,我要从限流算法到分布式架构全盘分析)…”一、为什么需要限流?核心矛盾:系统资源(CPU/内存/数据...
- Java面试题:服务雪崩如何解决?90%人栽了
-
服务雪崩是指微服务架构中,由于某个服务出现故障,导致故障在服务之间不断传递和扩散,最终造成整个系统崩溃的现象。以下是一些解决服务雪崩问题的常见方法:限流限制请求速率:通过限流算法(如令牌桶算法、漏桶算...
- 面试题官:高并发经验有吗,并发量多少,如何回复?
-
一、有实际高并发经验(建议结构)直接量化"在XX项目中,系统日活用户约XX万,核心接口峰值QPS达到XX,TPS处理能力为XX/秒。通过压力测试验证过XX并发线程下的稳定性。"技术方案...
- 瞬时流量高并发“保命指南”:这样做系统稳如泰山,老板跪求加薪
-
“系统崩了,用户骂了,年终奖飞了!”——这是多少程序员在瞬时大流量下的真实噩梦?双11秒杀、春运抢票、直播带货……每秒百万请求的冲击,你的代码扛得住吗?2025年了,为什么你的系统一遇高并发就“躺平”...
- 其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。
-
其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。比如上周有个小伙伴找我,五年经验但简历全是'参与系统设计''优化接口性能'这种空话。我就问他:你做的秒杀...
- PHP技能评测(php等级考试)
-
公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆。1.魔术函数有哪些,分别在什么时候调用?__construct(),类的构造函数__destruct(),类的析构函数__cal...
- 你的简历在HR眼里是青铜还是王者?
-
你的简历在HR眼里是青铜还是王者?兄弟,简历投了100份没反应?面试总在第三轮被刷?别急着怀疑人生,你可能只是踩了这些"隐形求职雷"。帮3630+程序员改简历+面试指导和处理空窗期时间...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)