美文网首页mongodb
【mongoDB】mongoDB的高可用、一致性

【mongoDB】mongoDB的高可用、一致性

作者: Bogon | 来源:发表于2022-06-25 00:10 被阅读0次

    一、MongoDB的高可用

    高可用是MongoDB最核心的功能之一,相信很多同学也是因为这一特性才想深入了解它的。
    那么本节就来说下MongoDB通过哪些方式来实现它的高可用,然后给予这些特性我们可以实现什么程度的高可用。

    相信一旦提到高可用,浮现在大家脑海里会有如下几个问题:

    1. 是什么:MongoDB高可用包括些什么功能?它能保证多大程度的高可用?
    2. 为什么:MongoDB是怎样做到这些高可用的?
    3. 怎么用:我们需要做些怎样的配置或者使用才能享受到MongoDB的高可用特性?

    那么,带着这些问题,我们继续看下去,看完大家应该会对这些问题有所了解了。

    MongDB复制集群

    MongoDB高可用的基础是复制集群,复制集群本质来说就是一份数据存多份,保证一台机器挂掉了数据不会丢失。
    一个副本集至少有3个节点组成:

    1. 有且仅有一个主节点(Primary):负责整个集群的写操作入口,主节点挂掉之后会自动选出新的主节点。
    2. 一个或多个从节点(Secondary):一般是2个或以上,从主节点同步数据,在主节点挂掉之后可被选举成新的主节点。
    3. 零个或1个仲裁节点(Arbiter):这个是为了节约资源或者多机房容灾用,只负责主节点选举时投票不存数据,保证能有节点获得多数赞成票。

    从上面的节点类型可以看出,一个三节点的复制集群可能是PSS或者PSA结构。
    PSA结构优点是节约成本,但是缺点是Primary挂掉之后,一些依赖 majority(多数)特性的写功能出问题,因此一般不建议使用。

    复制集群确保数据一致性的核心设计是:

    1. Journal日志:Journal日志是MongoDB的预写日志WAL,类似MySQL 的redo log,然后100ms一次将Journal日志刷盘。当然触发机制还有其它场景,这里仅仅是讨论异常场景下可能丢失多长时间的数据。更多详细的解释可以参考MongoDB的两种日志journal与oplog。

    2. Oplog:Oplog是用来做主从复制的,类似MySql里的binlog。MongoDB 的写操作都由Primary节点负责,Primary节点会在写数据时会将操作记录在Oplog中,Secondary节点通过拉取oplog信息,回放操作实现数据同步。

    3. Checkpoint:上面提到了MongoDB的写只写了内存和Journal日志(Journal日志是WAL日志),并没有做数据持久化到数据文件中,Checkpoint就是将内存变更刷新到磁盘持久化的过程。MongoDB会每60s一次将内存中的变更刷盘,并记录当前持久化点(checkpoint),以便数据库在重启后能快速恢复数据。

    4. 节点选举:MongoDB的节点选举规则能够保证在Primary挂掉之后选取的新节点一定是集群中数据最全的一个。

    从上面4点我们可以得出 MongoDB 高可用的如下结论:

    MongoDB宕机重启之后可以通过checkpoint快速恢复上一个60s之前的数据。
    MongoDB最后一个checkpoint到宕机期间的数据可以通过Journal日志回放恢复。
    Journal日志因为是100ms刷盘一次,因此至多会丢失100ms的数据
    (这个可以通过WriteConcern的参数控制不丢失,只是性能会受影响,适合可靠性要求非常严格的场景)

    如果在写数据开启了多数写,那么就算Primary宕机了也是至多丢失100ms数据(可避免,同上)。

    二、mongoDB的一致性

    分布式系统必须要面对的一个问题就是数据的一致性和高可用,针对这个问题有一个非常著名的理论就是CAP理论。
    CAP理论的核心结论是:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
    关于CAP理论在网上有非常多的论述,这里也不赘述。

    CAP理论提出了分布式系统必须面临的问题,但是我们也不可能因为这个问题就不用分布式系统。
    因此,BASE(Basically Available基本可用、Soft state软状态、Eventually consistent最终一致性)理论被提出来了。
    BASE理论是在一致性和可用性上的平衡,现在大部分分布式系统都是基于 BASE理论设计的,当然MongoDB也是遵循此理论的。

    选举和Raft协议

    MongoDB为了保证可用性和分区容错性,采用的是副本集的方式,这种模式就必须要解决的一个问题就是怎样快速在系统启动和Primary发生异常时选取一个合适的主节点。

    这里潜在着多个问题:

    1. 系统怎样发现Primary异常?

    2. 哪些Secondary节点有资格参加Primary选举?

    3. 发现Primary异常之后用什么样的算法选出新的Primary节点?

    4. 怎么样确保选出的Primary是最合适的?

    Raft协议

    MongoDB的选举算法是基于Raft协议的改进,Raft协议将分布式集群里面的节点有3种状态:

    leader:就是Primary节点,负责整个集群的写操作。
    candidate:候选者,在Primary节点挂掉之后,参与竞选的节点。只有选举期间才会存在,是个临时状态。
    follower:就是Secondary节点,被动的从Primary节点拉取更新数据。

    节点的状态变化是:正常情况下只有一个leader和多个flower,当leader挂掉了,那么flower里面就会有部分节点成为candidate参与竞选。
    当某个candidate竞选成功之后就成为新的leader,而其他candidate回到flower状态。
    具体状态机如下:


    image.png

    Raft协议中有两个核心RPC协议分别应用在选举阶段和正常阶段:

    请求投票:选举阶段,candidate向其他节点发起请求,请求对方给自己投票。
    追加条目:正常阶段,leader节点向follower节点发起请求,告诉对方有数据更新,同时作为心跳机制来向所有follower宣示自己的地位。
    如果follower在一定时间内没有收到该请求就会启动新一轮的选举投票。

    投票规则

    Raft协议规定了在选举阶段的投票规则:
    一个节点,在一个选举周期(Term)内只能给一个candidate节点投赞成票,且先到先得。
    只有在candidate节点的oplog领先或和自己相同时才投赞成票。

    选举过程

    一轮完整的选举过程包含如下内容:

    1. 某个/多个follower节点超时未收到leader的心跳,将自己改变成candidate 状态,增加选举周期(Term),然后先给自己投一票,并向其他节点发起投票请求。

    2. 等待其它节点的投票返回,在此期间如果收到其它candidate发来的请求,根据投票规则给其它节点投票。

    3. 如果某个candidate在收到过半的赞成票之后,就把自己转换成leader状态,并向其它节点发送心跳宣誓即位。

    4. 如果节点在没有收到过半赞成票之前,收到了来自leader的心跳,就将自己退回到follower状态。

    5. 只要本轮有选出leader就完成了选举,否则超时启动新一轮选举。

    catchup(追赶)

    以上就是目前掌握的MongoDB的选举机制,其中有个问题暂时还未得到解答,就是最后一个,怎样确保选出的Primary是最合适的那一个?

    因为,从前面的协议来看,存在一个逻辑bug:由于follower转换成candidate是随机并行的,再加上先到先得的投票机制会导致选出一个次优的节点成为Primary。

    针对Raft协议的这个问题,下来查询了一些资料,结论是:

    Raft协议确实不保证选举出来的Primary节点是最优的。

    MongoDB通过在选举成功,到新Primary即位之前,新增了一个 catchup(追赶)操作来解决。
    即在节点获取投票胜利之后,会先检查其它节点是否有比自己更新的oplog,如果没有就直接即位,如果有就先把数据同步过来再即位。

    主从同步

    MongoDB的主从同步机制是确保数据一致性和可靠性的重要机制。其同步的基础是oplog,类似MySQL的binlog,但是也有一些差异,oplog虽然叫log但并不是一个文件,而是一个集合(Collection)。

    同时由于 oplog 的并行写入,存在尾部乱序和空洞现象,具体来说就是oplog里面的数据顺序可能是和实际数据顺序不一致,并且存在时间的不连续问题。

    为了解决这个问题,MongoDB采用的是混合逻辑时钟(HLC)来解决的,HLC不止解决乱序和空洞问题,同时也是用来解决分布式系统上事务一致性的方案。

    主从同步的本质实际上就是,Primary节点接收客户端请求,将更新操作写到oplog,然后Secondary从同步源拉取oplog并本地回放,实现数据的同步。

    同步源选取

    同步源是指节点拉取oplog的源节点,这个节点不一定是Primary,链式复制模式下就可能是任何节点。
    节点的同步源选取是一个非常复杂的过程,大致上来说是:

    1. 节点维护整个集群的全部节点信息,并每2s发送一次心跳检测,存活的节点都是同步源备选节点。

    2. 落后自己的节点不能做同步源:就是源节点最新的opTime不能小于自己最新的opTime。

    3. 落后Primary 30s以上的不能作为同步源。

    4. 太超前的节点不能作为同步源:就是源节点最老的opTime不能大于自己最新的opTime,否则有oplog空洞。

    在同步源选取时有些特殊情况:

    用户可以为节点指定同步源。
    如果关闭链式复制,所有Secondary节点的同步源都是Primary节点。
    如果从同步源拉取出错了,会被短期加入黑名单。

    oplog拉取和回放

    整个拉取和回放的逻辑非常复杂,这里根据自己的理解简化说明,如果想了解更多知识可以参考《MongoDB复制技术内幕》

    节点有一个专门拉取oplog的线程,通过Exhausted cursor从同步源拉取 oplog。拉取下来之后,并不会执行回放执行,而是会将其丢到一个本地
    的阻塞队列中。

    然后有多个具体的执行线程,从阻塞队列中取出oplog并执行。

    在取出过程中,同一个Collection的oplog一定会被同一个线程取出执行,线程会尽可能的合并连续的插入命令。

    整个回放的执行过程,大致为先加锁,然后写本店oplog,然后将oplog刷盘(WAL机制),最后更新自己的最新opTime。

    三、参考

    MongoDB全方位知识图谱
    https://mp.weixin.qq.com/s/bhXPnLotUoQYJI61eORCfA

    相关文章

      网友评论

        本文标题:【mongoDB】mongoDB的高可用、一致性

        本文链接:https://www.haomeiwen.com/subject/mcmnmrtx.html