参考:https://www.cnblogs.com/littleatp/p/8562842.html
https://www.cnblogs.com/ilifeilong/p/14347008.html
MongoDB副本集
MongoDB副本集是由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点。客户端的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持副本集内所有成员存储相同的数据集,实现数据的高可用。
1. 副本集角色
- 主节点Primary:接受所有的写请求,然后把修改同步到所有Secondary,一个副本集只能有一个Primary节点,当Primary挂掉后,其他的Secondary或者Arbiter节点会重新选举出来一个主节点;
- 副本节点Secondary
- 仲裁者Arbiter:不保存数据,不能作为主节点,只进行选主投票,使用Arbiter可以减轻数据存储到硬件需求;
2. 两种架构模式
2.1 PSS
Primary + Secondary + Seconda,该模式下副本集节点数比虚伪奇数,使得选主投票时出现大多数(节点总数N,大多数N/2 + 1)的情况。
2.2 PSA
Primary + Secondary + Arbiter,偶数个数据节点 + 一个Arbiter节点。
3. 选举机制
3.1 特殊角色
- Arbiter:只参与投票,不能被选举为Primary,不从Primary同步数据,当副本集成员数为偶数时,最好加入一个Arbiter节点(轻量级),以提升副本集可用性。
- Priority0:该节点的选举优先级为0,不会被选举为Primary;
- Vote0:在MongoDB 3.0中,副本集成员最多为50个,参与Primary选举投票的成员最多有7个,其他成员的vote属性必须设置为0,即不参与投票;
- Hidden:不能被选为Primary(Priority为0),并且对客户端不可见。因Hidden节点不接受客户端请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响副本集的服务;
- Delayed:该节点必须是Hidden节点,并且其数据落后于Primary节点一段时间。因Delayed节点的数据比Primary落后一段时间,当错误或者无效的数据写入Primary时,可通过Delayed节点的数据来恢复到之前的时间点;
3.2 触发选举的条件
- 初始化一个副本集时;
- 从库不能连接到主库(默认超过10s),由从库发起选举;
- 主库放弃Primary角色;
3.3 选举
- 当副本集的从节点ec1-notificationdb-02发现自己无法与主节点ec1-notificationdb-01取得联系并且该从节点有成为主节点的条件时,从节点ec1-notificationdb-02会向所有能到达的其他从节点ec1-notificationdb-03/04/05发送选举通知,请求选举自己为主节点;
- 当从节点ec1-notificationdb-0/04/053收到该请求后,会判断该从节点ec1-notificationdb-02可能并不是一个合适的候选者,例如数据副本落后于自己,并且ec1-notificationdb-03/04/05能够和主节点取得联系,只是从节点ec1-notificationdb-02自己无法到达而已,这种情况下,其他从节点会拒绝该申请节点ec1-notificationdb-02的选举请求;
- 如果其他从节点ec1-notificationdb-03/04/05并没有反对的理由,那么这些从节点将会对ec1-notificationdb-02的选举请求进行投票,如果ec1-notificationdb-03/04/05中超过半数同意ec1-notificationdb-02的选举请求,那么ec1-notificationdb-02选举成功并且将自己转化为主节点。如果没有得到超过半数的同意,那么在一段时间以后,从节点ec1-notificationdb-02会重新发起选举请求;
- 主节点ec1-notificationdb-01将会一直处于Promary角色,如果它无法和副本集中大部分从节点通信,该主节点会自动降级成为从节点;
4. 数据同步
Primary与Secondary之间通过oplog来同步数据,Primary上的写操作完成后,会向特殊的local.oplog.rs特殊集合写入一条oplog,Secondary不断的从Primary获取新的oplog并应用。因 oplog 的数据会不断增加,local.oplog.rs 被设置成为一个 capped 集合,当容量达到配置上限时,会将最旧的数据删除掉。另外考虑到 oplog 在 Secondary 上可能重复应用,oplog 必须具有**幂等性。
如下 oplog 的格式,包含 ts、h、op、ns、o 等字段。
/*ts:时间戳; h:全局唯一标示; v:oplog版本信息; op:操作类型; ns:操作针对的集合; o:操作内容;
*/
{"ts" : Timestamp(1446011584, 2), "h" : NumberLong("1687359108795812092"),"v" : 2,"op" : "i","ns" : "test.nosql","o" : { "_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb", "score" : "100" }
}
Secondary 初次同步数据时,会先执行 init sync,从 Primary(或其他数据更新的 Secondary)同步全量数据,然后不断通过执行tailable cursor从 Primary 的 local.oplog.rs 集合里查询最新的 oplog 并应用到自身。
5. 回滚
如果一个主节点在写入一条数据后并没有来得及将该数据同步到从节点,此刻主节点突然宕机,这时从节点发起选举请求,选举出新的主节点,那么新的主节点是没有老的主节点那一条数据的。
如上图所示,在数据"_id:4,username:user004"还没有来得及写入从节点时,主节点因网络故障无法与所有从节点通信,此时从节点将会发起选举请求,选举出主节点,而原有的主节点网络恢复后会被降级成从节点,最终新的主节点的架构图如下:
这里需要特别注意,原来的主节点以从节点的身份加入副本集后,用自己最新的数据"_id:4, username:user004"和新的主节点对比,发现新的主节点并没有该条数据,于是再用自己较老的数据"_id:3, username:user003"和新的主节点数据对比,发现新的主节点数据和自己匹配,那么该从节点将会回滚数据至"_id:3, username:user003",该版本以后的数据会被存放至数据目录的rollback里,然后开始同步新的主节点的数据。