参考资料
官网:www.mongodb.org
中文社区:www.mongoing.com
在线教程:https://university.mongodb.com/
mongodb支持传统的master-slave架构。没有自动故障转移功能,需要指定master和slave端。建议使用复制集架构,复制集架构比复制架构更好维护,功能更强。
一、基本概念
复制集是由一组拥有相同数据集的 mongod 实例组成的。其中的一个节点为主节点(Primary),所有的写请求都是在它上面完成的。而其他的节点都是从节点(secondary),从节点接收从主节点上传来的操作并应用,并以此来保证其与主节点的数据集一致。
主节点:接收所有来自客户端的写操作。一个复制集只能有一个主节点。由于在一个复制集中只有一个成员能够接收写操作,复制集为所有来自主节点的读提供了 严格的一致性校验 。主节点通过将所有数据集的变动记录到 oplog 中以支持复制的实现
从节点:将主节点上的oplog复制过来并应用这些操作来修改其自己的数据集以确保从节点的数据集与主节点的数据集一致。一旦主节点不可用了,复制集就会将一个从节点选举成为新的主节点。客户端默认是从主节点进行读操作,但是客户端也可以通过指定 复制集读选项 来将读操作发送给从节点。需要注意的是,在从节点上进行读操作时,所获得的数据可能不是此时主节点上的值。
投票节点:我们也可以为复制集新增一个额外的 mongod 实例作为 投票节点 。该节点中并不包含数据集,投票节点的作用仅仅是在选举过程中参与投票。当复制集的成员个数为偶数时,添加一个投票节点可以防止平局的出现,通过多数选票来选举出新的主节点。由于投票节点仅提供投票功能,故无需一个专用的物理机。
投票节点将只做投票使用。当主节点降职变为从节点的时候,其他的一个从节点将在选举中被推选为主节点。
二、复制集架构
最基础的复制集架构是由三个成员组成的。这样的架构为复制集提供了冗余与故障切换的余地。根据应用的需求来设计复制集的架构,尽量避免不必要的复杂化。
复制集应含有奇数个成员
奇数个成员的存在确保了复制集可以正常的选举出主节点。如果副本集现有偶数个成员,那么请增加一个投票节点以保证其成员个数为奇数。
为特殊需求使用隐藏节点和延时节点。
新增隐藏节点或是延时节点来为特殊需求提供服务,比如备份或是报表。
以读为主的架构的负载均衡
若业务带来的大量的读请求,我们可以通过做读写分离来提升复制集的读能力。随着业务的扩展,我们可以通过在其他数据中心新增从节点的方式来提高冗余能力与可用性。
决定复制集各成员的分布与功能
物理位置上的节点分布
在其他数据中心拥有至少一个复制集节点可以很好地在主数据中心出问题时为数据提供安全性保障。将这类节点的priority设置为0,以防其升职为主节点。
保证在一个数据中心中拥有多数节点
当复制集在多个数据中心拥有节点,且各数据中心网络隔离时,为了保证数据的复制与传输,各节点之间需要能够正常沟通。
在选举中,各节点需要能够互相沟通来保证其多数性。为了保证复制集节点能够保持多数且能够正常的选举出主节点,我们需要保证一个数据中心拥有复制集中的多数节点。
三、故障切换
复制集通过选举来从当前主节点不可用的困境中恢复。
一旦当前主节点不可用了,复制集就会进行选举并推选出一个新的主节点。
之前的主节点在故障切换后重新回归复制集时将会发生写操作的回滚。回滚只会发生在主节点的写操作没能成功在从节点上应用就辞职的情况下。当主节点重新以一个从节点加入复制集,它将进行
“回滚”,其上得写操作将与复制集中其他成员的保持一致。
MongoDB会尽量避免回滚的发生。回滚如果确实发生了,往往是由于网络导致的。从节点如果无法跟上之前主节点上的写操作的吞吐,那么将会加剧回滚的影响范围。
当主节点在从节点完成写操作的复制后才辞职的或是主节点一直是可用的或是与多数节点可以沟通的,将不会发生回滚。
选取回滚的数据
当回滚发生后,管理员需要决定是恢复回滚的数据还是忽视它。MongoDB将回滚的数据以 BSON 文件的形式写到数据库 dbPath 文件夹中的 rollback/ 目录。回滚数据文件的命名是按照以下规则进行的
<database>.<collection>.<timestamp>.bson
如:records.accounts.2011-05-09T18-10-04.0.bson
可通过设置 复制集的安全写级别 确保写操作应用到了整个复制集来避免回滚。
影响选举的因素
1.心跳检测
复制集成员每两秒向复制集中其他成员进行心跳检测。如果某个节点在10秒内没有返回,那么它将被标记为不可用。
2.连接
如果复制集中的某个节点不能连接上其他多数节点,那么它将不能升职为主节点。在选举中,多数是指多数投票而不是多数节点个数。如果复制集是由三个节点组成的,且三个节点均可投票,只要其中两个节点能够互相沟通那么复制集就能选举出新的主节点。如果有两个节点不可用了,那么剩下的节点将为从节点,因为它不能与复制集中多数节点进行沟通。
如果两个从节点不可用了,剩下的主节点将降职为从节点。
3.网络隔离
网络隔离影响了选举中多数选票的结构。如果主节点不可用了,且每个相互隔离的网络中都没有多数选票的出现,那么复制集将 不会选举出新的主节点。复制集将变为只读的。为了避免这种情况的出现,我们需要将多数节点置于主数据中心,少数节点放于其他数据中心。
触发选举的情况
当复制集中没有主节点可用的时候将触发选举,比如:
1.新复制集的初始化。
2.一个从节点无法与主节点进行连接。当从节点们无法与主节点进行沟通的时候将会触发选举。
3.主节点辞职了
主节点将在以下几种情况下辞职:
1.在接收到 replSetStepDown 命令后。
2.现有的某个从节点在选举中合格,且它又有更高的优先级。
3.当主节点无法与复制集中多数节点进行沟通的时候。
4.有些情况下,在我们需要修改一些复制集配置的时候会触发选举,导致主节点辞职
注意:当主节点辞职后,它将关闭所有已经建立的连接来确保客户端不会在从节点中进行写操作。这将对客户端对复制集的架构获取与防止回滚提供帮助。
我们具体来看下选举的过程
心跳检测
假设我们有三个节点的replica sets:a、b、c。在replica sets结构中,这三个节点每2秒会各自向其它两个节点发送一个心跳检测请求。比如a节点向b和c节点各发送了一个心跳检测请求;正常情况下,b、c会返回一个包含自身信息的回复包,回复包中主要包括的信息:它们现在是什么角色(primary 还是 secondary),他们是否能够在必要的时候成为 primary,以及他们当前时钟时间等等。
a节点在收到回复包后,会用这些信息更新自己的一个状态映射表,更新的内容包括:是否有新的节点加入或有老的节点宕掉,这个请求的网络传输时间等等。
而当a节点的映射表发生了变化,那a会进行下面的逻辑判断:如果a是 primary,而另外一个节点出现故障,那么它会查看自己是否还能和集群中大多数节点进行通信,如果不能与大多数节点通信,他会把自己从 primary 降级为 secondary。(在replica sets中,primary 必须能够和集群中的大多数节点进行通信,以免发生网络断开形成两个或多个节点群各自为政的情况,这样会影响到数据的一致性)
关于降级
在 MongoDB 中,写操作默认通过 fire-and-forget 的模式来进行,也就是说写操作通常不关心是否成功,发完请求后客户端就认为成功了。但如果这时候 primary 进行降级操作,那么客户端并不知道这时候 primary
已经降级成为 secondary 了,客户端可能还会将后续的写操作发送给这个节点。这时候刚刚降级的这个 secondary 可以发送一个包说“我已经不是 primary 了”,但是我们上面说过了,客户端根本就无视你这个包。所以客户端根本不知道这次写入已经失败了。
对于这个问题,MongoDB开发人员已经考虑到了,解决方案是,在一个 primary 降级成为 secondary 后,它会将原来的所有连接关闭。这样客户端在下一次写入的时候就会出现 socket 错误。而客户端在发现这个错误之后,就会重新向集群获取新的 primary的地址,并将后续的写操作都往新的服务器上写入。
选举
再来看心跳监测请求:如果a是一个 secondary,那么a会定时检测是否需要选举自己成为 primary。其检测内容包括:
1.是否集群中有其它节点认为自己是 primary?
2.a节点自己是否已经是 primary?
3.a节点自己是否有资格成为 primary?
如果这三个问题中的任何一个回答是肯定的,那么a节点就不会试图把自己变成primary。(即:只有当a节点是一个能够当 primary 的secondary,并且其它节点都不是primary时,a才会发起选举并选自己为primary)
而当a发现现在需要一个 primary 并且自己又正好可以充当时,它就会发起一轮选举:a节点会向b、c节点各发起一个请求包,告知他们”我认为我可以接管 primary 的角色,你们觉得怎么样?“
当b和c收到上面的请求包时,他们会进行下面几项检测:
1.他们是否已经支持集群中有一个primary了?
2.他们自己的数据是否比a节点更新?
3.是否有其它节点的数据比a节点更新?
如果上面条件有任何一个满足,那么他们都会认为a不够资格成为 primary,他们会发送一个返回包告知a说”停止选举!“。而如果三个条件都不成立,也就是说他们认为目前集群中确实没有 primary,并且a的数据又是最新的,那么他们会发送返回包告知a说”没问题“。
如果a收到”停止选举!“的返回,那么他会马上停止选举并保持自己为 sencondary 状态。
如果a收到所有其它节点都返回说”没问题“,那么他会进入选举过程的第二阶段。
在第二阶段中,a会向其它节点发送一个包,说”我宣布我已经是 primary 了“。这时候,b和c节点再进行一些最终的确认:上面的判断过的所有条件是否依然表明a可以做 primary,如果确实如此,那么他们会在本轮 primary 选举中向a出赞成票。并且他们投完赞成票后,30秒内不会再做其它投票决定。
上面是说如果第二次确认还是通过的情况,那么如果最终确认没有通过呢。他们会投一个反对票,反对a成为 primary,如果有反对票产生,那么这一轮选举就失败了。a还是保持 secondary 的身份。
假设一种情况,如果b给a投了赞成票,而c给a投了反对票。那这时候b由于投了赞成票,它在30秒内不能再进行投票。所以如果这时候c发起选举想让自己成为 primary,那么c这时候必须要获得a的赞成票。因为这时候b不能投票,为了获取多数票,c必须获得a的赞成票。
所以投票的规则是这样的:如果没有人投反对票,并且赞成票的比例过半,那么本轮选举对象就能够成为 primary。
四、复制集的读与写
默认情况下,应用程序将直接在复制集的主节点上进行读操作。
在主节点上进行读操作确保了读操作返回的数据都是最新的数据。但是,在对数据一致性要求没那么严格的情况下,如果将部分或是所有的读操作分发到复制集的从节点上去处理,能够提升读的性能也能降低应用程序的等待时间。
为了确保从从节点上进行读操作时的数据一致性,我们可以配置客户端来确保写操作在应用到复制集中所有节点后才算成功完成。
MongoDB的驱动支持5种复制集读选项。
复制集读选项模式 | 详细说明 |
primary | 默认模式,所有的读操作都在复制集的 主节点 进行的。 |
primaryPreferred | 在大多数情况时,读操作在 主节点 上进行,但是如果主节点不可用了,读操作就会转移到 从节点 上执行。 |
secondary | 所有的读操作都在复制集的 从节点 上执行。 |
secondaryPreferred | 在大多数情况下,读操作都是在 从节点 上进行的,但是当 从节点 不可用了,读操作会转移到 主节点 上进行。 |
nearest | 读操作会在 复制集 中网络延时最小的节点上进行,与节点类型无关。 |
通过配置来找到适合自己业务的架构。