当前位置:首页 > 问答 > 正文

Redis集群里选主那些事儿,算法细节和实现上的一些小技巧分享

主要参考了Redis官方文档关于Raft的讨论、Redis核心开发者Salvatore Sanfilippo(antirez)的博客文章《Redis的过去、现在和未来》中关于集群的展望,以及一些深入分析Redis源码的技术博客,如“Redis设计与实现”等)

说起Redis集群里选主这件事儿,其实在早期和现在是有变化的,最早Redis哨兵模式下的选主和后来Redis Cluster模式下的选主,思路不太一样,咱们这里主要聊现在更常用的Redis Cluster模式下的选主,它背后的想法挺有意思的。

为啥要选主?简单说就是“群龙不能无首”

Redis集群是一堆Redis实例(节点)一起干活,数据被分成很多份(槽),每个节点负责存一部分,但为了保证数据不丢,每个分片的数据通常会有好几个副本,比如一个主节点带着两个从节点,主节点负责读写,从节点负责备份,如果主节点突然挂掉了,那这个分片不就没人能写了吗?必须得从剩下的从节点里,赶紧挑一个出来当新的主节点,继续提供服务,这个“挑”的过程,就是选主。

选主的核心算法:不是少数服从多数,是“大多数”同意

你可能听说过Paxos、Raft这些特别复杂的选举算法,Redis Cluster用的不是它们,但它自己有一套逻辑,核心思想跟“投票”和“大多数”原则很像。

  1. 故障发现:不是一个人说了算 一个从节点觉得主节点可能挂了(比如连不上了),它不会马上行动说自己要当主,因为它觉得挂了,可能只是网络抽风,自己掉线了,那怎么办呢?它得去问问集群里的其他节点:“哎,你们觉得XXX主节点还活着吗?” 这个过程是借助一种叫“ Gossip ”的协议,像闲聊一样互相交换信息。

    Redis集群里选主那些事儿,算法细节和实现上的一些小技巧分享

  2. 拉票竞选:需要得到足够多的信任票 当一个从节点(我们叫它候选人)从大多数主节点那里得到确认,说原主节点确实挂了,它就有资格发起竞选了,它会向集群里所有它能联系到的主节点广播一条消息:“兄弟们,原主节点XXX已经确认下线了,我的数据也比较新,我想当新的主节点,请投我一票!” 这里有个关键点:只有主节点有投票权,一个集群有N个主节点,候选人必须拿到至少 N/2 + 1 张赞成票(也就是超过半数),才能成功当选,比如集群有5个主节点,就需要至少3票。

  3. 为什么是“大多数”主节点? 这主要是为了防止“脑裂”,想象一下,如果网络出现故障,把集群分成了两半,两边可能都会各自选出一个主节点来,如果选举门槛很低,比如只需要一票同意,那就会产生两个“主节点”,都对外提供服务,数据就乱套了,而要求必须得到“大多数”主节点的同意,就意味着在网络分裂的情况下,最多只有一边能凑够“大多数”的票数(因为节点总数是固定的),另一边因为节点数少,永远凑不够票,这样就保证了整个集群在任何时候最多只有一个合法的老大,避免了数据冲突。

实现上的一些小技巧和要注意的地方

光有算法还不够,实际做的时候有很多细节要考虑,这些算是Redis开发者们总结下来的一些技巧:

Redis集群里选主那些事儿,算法细节和实现上的一些小技巧分享

  1. 给点“延迟”,避免混战 当主节点失效,可能不止一个从节点同时发现并开始竞选,如果大家同时拉票,票数可能分散,导致谁都选不出来,Redis在这里用了一个小技巧:让数据更更新的从节点稍微早点竞选,数据旧点的晚点竞选。 怎么实现呢?就是从节点会等待一小段时间,这个等待时间和它复制数据的进度有关(复制偏移量),谁的数据最新(最接近原主节点),谁等的时间就越短,它就更有机会先发出投票请求,从而更容易当选,这保证了新主节点拥有最完整的数据。

  2. 投票的“保质期” 一个主节点在给某个分片的选举中,只能投一次票,而且这个投票是有“保质期”的,会过期,这是为了防止原主节点只是短暂网络故障后又恢复了,但这边已经选出了新主,造成两个主节点并存,有了投票过期机制,即使原主节点恢复,它想重新夺权,也因为拿不到足够的“新鲜”选票而失败,只能乖乖变成新主的从节点。

  3. 依赖当前存活的主节点数 选举时计算“大多数”所依据的总主节点数,不是集群配置的固定值,而是当前候选人实际能联系到的、并且也被这些节点确认是活跃的主节点数量,这使系统在网络分区时更具弹性,能在每个分区内基于当前情况做出合理的选举判断(最终只有包含大多数主节点的分区能选举成功)。

  4. epoch(配置纪元)—— 集群的“朝代”编号 这是实现里一个非常巧妙的设计,每次成功的选举,或者任何导致集群配置变更的大事发生(比如节点增删),都会使一个全局的、单调递增的数字加1,这个数字就叫epoch,它就像是王朝的更替年号,任何节点在发出投票或者接受新主时,都会检查epoch,节点只会响应更高epoch的请求,并更新自己的epoch,这能有效地让集群所有节点对“谁是最新的主”达成一致,废弃掉旧的、可能冲突的配置信息,是保证集群状态最终一致性的关键。

Redis集群的选举机制,用相对简单直观的“投票”和“大多数”原则,再配合上等待延迟、投票有效期、动态节点计数和配置纪元这些实用的技巧,在保证数据一致性和可用性之间取得了很好的平衡,让集群能够稳定可靠地应对节点故障。