MongoDB 复制集(二) 选举 自动故障切换

时间:2023-03-08 15:47:54
MongoDB 复制集(二) 选举 自动故障切换

一   复制集的高可用性简介

      复制集通过故障自动切换来实现高可用性,当主节点出现故障的时候,从节点可以通过选举成为主节点,而这个过程在大多数当情况下是自动进行的,不需要手动干预。在某些情况下,故障自动切换需要数据回滚。

      复制集部署的方式(复制集成员数量、物理因素,如带宽 复制集成员地理位置等)可能会影响自动切换的效率。为了提高自动切换的效率,我们应该将复制集的大多数成员放到一个核心的数据中心来进行管理,在复制集里多放几个从节点,当主节点失效的时候,不但保证有可用的从节点可以使用,而且还可以防止因网络故障的发生,隔离大多数复制集成员的通信。

   自动故障切换主要包括两大过程:选举 和 回滚

   选举在复制集的操作中扮演着非常重要的作用,选举过程是要花费时间的,在选举过程中,复制集是没有主节点的,整个复制集是无法接受客户端的请求,所以MongoDB尽量避免选举的进行。那么什么情况下才会进行选举呢?
选举的发生:当主节点不可用的时候;复制集初始化(initiating)的时候.

二  影响选举的因素

   1.心跳(Heartbeats)
       心跳:复制集成员每隔两秒互相发送一次心跳请求包(其是就是ping对方),如果在10秒钟内没有收到对方成员对心跳包回复的话,就认为它出现了故障。

   2.优先级比较(
Priority Comparisons
)
       成员的优先级会影响到选举的过程,成员们会优先将自己的票投给优先级高的成员。
       拥有0优先级的成员不会被选为主节点,因为他们没有被选举权,
不会被投票
       只要当前的主节点拥有最高的优先级且其操作日志条目内容也是最新的,那就不会进行选举。这时
如果有一个更高优先级的成员加入且其操作日志所记录最新操作时间和当前主节点记录的时间相差小于10秒的时候,这时就会进行选举,给高优先级节点提供一个成为主节点的
机会。
 

    3.操作时间(
Optime)
      操作时间指的是一个成员最后一次执行操作日志时的时间。如果一个成员比其他的成员操作时间都最新(most recent)的话,就有可能被选为主节点。

     4.连通性(
Connections)
      复制集中的一个成员只有和大多数成员保持联通,得到大多数人的投票才可以成为主节点,否则会自动降级成为从节点。

      例如:对于一个拥有三个成员的复制集,每个成员都可以投票,那么只有其中的两个人可以保持联通的话,复制集就可以进行一次选举。如果其中两个变的不可用,剩下的那个节点如果是从节点的话,那么它仍然还是一个从节点,因为它不能和其他的成员保持连通。同样的道理如果该节点是主节点的话,也会自动的降级成为从节点,因为它不能和别的成员保持连通,失去了成为主节点的资格.这样的话该复制集就变成只读了。

     5.网络分区(
Network Partitions
      由于网络分区,如果一个主节点不可用的话,其余
大多数
从节点又不能连通的话,复制集就无法进行选举。所以尽量将大部分复制集成员放到一个数据中心中,而将一小部分放到另外一个。

三 选举的过程

1.心跳检测

       假设我们有三个节点的replica set:X,Y和Z节点。在replica sets结构中,这三个节点每2秒会各自向其它两个节点发送一个心跳检测请求。比如X节点向Y和Z节点各发送了一个心跳检测请求,在正常情况下,Y、Z会返回一个包含自身信息的回复包,回复包中主要包括了下面一些信息:它们现在是什么角色(primary 还是 secondary),他们是否有资格成为成为 primary,以及他们当前时戳等等。
       X节点在收到回复包后,会用这些信息更新自己的一个状态映射表,更新的内容包括:是否有新的节点加入或有老的节点宕掉,这个请求的网络传输时间等等。
       
而当X节点的映射表发生了变化,那X会进行下面的逻辑判断:如果X是 primary,而另外一个节点出现故障,那么它会确认自己是否还能和集群中大多数节点进行通信,如果不能与大多数节点通信,那么他会把自己从 primary 降级为 secondary。(注意:在replica sets中,primary 必须能够和集群中的大多数节点进行通信,以免发生网络断开形成两个或多个节点群各自为政的情况,这样会影响到数据的一致性。)

 2.关于降级


       在节点从 primary 降级为 secondary 的过程中,会有一些问题出现。在 MongoDB 中,写操作默认是通过 fire-and-forget(发射即不管,相当于自动制导炸弹) 的模式来进行的,也就是说写操作通常不关心是否成功,发完请求后客户端就认为成功了。但如果这时候 primary 进行降级操作,那么客户端并不知道这时候 primary 已经降级成为 secondary 了,客户端可能还会将后续的写操作发送给这个节点。这时候刚刚降级的这个 secondary 可以发送一个包说“我已经不是 primary 了”,但是我们上面说过了,客户端根本就无视你这个包。所以客户端根本不知道这次写入已经失败了。
      对于这个问题,你可能会说”那我们每次都使用安全写入不就行了“(安全写入意思是说等待服务器返回成功后客户端才认为写成功了),但是很明显,这非常不靠谱。所以我们的做法是,在一个 primary 降级成为 secondary 后,它会将原来的所有连接关闭。这样客户端在下一次写入的时候就会出现 socket 错误。而客户端在发现这个错误之后,就会重新向集群获取新的 primary 的地址,并将后续的写操作都往新的服务器上写入。

 3.选举

我们回头再来看心跳监测请求:如果X是一个 secondary,那么X会定时检测是否需要选举自己成为 primary,即使自己的状态映射表没有发生改变。其检测内容包括:是否集群中有其它节点认为自己是 primary?X节点自己是否已经是 primary?X节点自己是否有资格成为 primary?如果这三个问题中的任何一个回答是否定的,那么X节点就不会试图把自己变成primary。(也就是说,只有当X节点是一个能够当 primary 的secondary,并且其它节点都不是primary时,X才会发起选举并选自己为primary)

       而当X发现现在需要一个 primary 并且自己又正好可以充当时,它就会发起一轮选举:X节点会向Y、Z节点各发起一个请求包,告知他们”我认为我可以接管 primary 的角色,你们觉得怎么样?当Y和Z收到上面的请求包时,他们会进行下面几项检测:他们是否已经知道集群中有一个 primary了?他们自己的数据是否比X节点更新?是否有其它节点的数据比X节点更新?如果上面条件有任何一个满足,那么他们都会认为X不够资格成为 primary,他们会发送一个返回包告知X说”停止选举!“。而如果三个条件都不成立,也就是说他们认为目前集群中确实没有 primary,并且X的数据又是最新的,那么他们会发送返回包告知X说”没问题“。

     
如果X收到”停止选举!“的返回,那么他会马上停止选举并保持自己为 sencondary 状态。
     如果X收到所有其它节点都返回说”没问题“,那么他会进入选举过程的第二阶段。

      在第二阶段中,X会向其它节点发送一个包,说”我宣布我已经是 primary 了“。这时候,Y和Z节点再进行一些最终的确认:上面的判断过的所有条件是否依然表明X可以做 primary,如果确实如此,那么他们会在本轮 primary 选举中向X出赞成票。并且他们投完赞成票后,30秒内不会再做其它投票决定。
     上面是说如果第二次确认还是通过的情况,那么如果最终确认没有通过呢。他们会投一个反对票,反对X成为 primary,如果有反对票产生,那么这一轮选举就失败了。X还是保持 secondary 的身份。
       
下面我们假设一种情况,如果Y给X投了赞成票,而Z给X投了反对票。那这时候Y由于投了赞成票,它在30秒内不能再进行投票。所以如果这时候Z发起选举想让自己成为 primary,那么Z这时候必须要获得X的赞成票。因为这时候Y不能投票,为了获取多数票,Z必须获得X的赞成票。
所以投票的规则是这样的:如果没有人投反对票,并且赞成票的比例过半,那么本轮选举对象就能够成为 primary。 


注:1.引发投票选举的事件:
               复制集进行初始化的时候
               当从节点失去与主节点的联系,号召大家来选举投票
               主节点进行降级
        2.引发节点进行降级的事件:
               当主节点收到replSetStepDown 命令的时候
               如果一个从节点拥有资格成为主节点,有比主节点更高优先级
               当主节点和大多数从节点无法进行连通的时候
         3.   当主节点变得不可用的时候会自动关闭和客户端得所有连接,这样可以保持客户端和数据库得数据一致性。
        4.Priortity0 成员不会触发选举操作,即使它们无法连接到主节点