进行,也就是说写操作通常不关心是否成功,发完请求后客户端就认为成功了。但如果这时候 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 |
读操作会在 复制集 中网络延时最小的节点上进行,与节点类型无关。 |
通过配置来找到适合自己业务的架构。
?