简介
一个复制集中Primary节点上能够完成读写操作,Secondary节点仅能用于读操作。Primary节点需要记录所有改变数据库状态的操作,这些记录保存在oplog中,这个文件存储在local数据库,各个Secondary节点通过此oplog来复制数据并应用于本地保持本地的数据与主节点的一致。oplog具有幂等性,即无论执行了多少次,其结果始终保持一致,这一点要优于mysql的二进制日志更好用。
oplog的组成结构
{
"ts": Timestamp(1446011590),
"h": NumberLong("16873591097812095"),
"v": 2,
"op": "i",
"ns": "test.nosql",
"o": {
"_id": ObjectId("56032c0b085795912092"),
"name": "mongodb",
"score": "10"
}
}
ts:操作时间,当前timestamp + 计数器,计算器每秒钟都会被重置
h:操作的全局唯一标识
v:oplog版本信息
op:操作类型
- i:插入操作
- u:更新操作
- d:删除操作
- c:执行命令(入createDatebase,dropDatabase)
n:空操作,特殊用途
ns:操作针对的集合
o:操作内容
o2:更新查询条件,仅update操作包含着字段
复制集数据同步分为初始化同步和keep复制同步。初始化同步指全量从主节点同步数据,如果Primary节点数据量比较大同步时间比较长 。而keep复制指初期同步后,节点之间的实时同步一般是增量同步。
初始化同步有以下两种情况会触发:
- Secondary第一次加入。
- Secondary落后的数据量超过了oplog的大小,这样也会被全量复制。
MongoDB的Primary节点选举基于心跳触发。一个复制集N个节点中的任意两个节点维持心跳,每个节点维护其他N-1个节点的状态。
心跳检测
整个集群需要保持一定的通信才能知道哪些节点活着哪些节点挂掉,mongo节点会向集群中其他的节点每隔2秒就发送一个ping包,如果被发送的节点10秒内都没有响应就被标记为不可用。每个节点内部都会保存一个状态映射表,表明当前节点是什么角色,日志时间戳等关键信息。如果主节点发现自己无法与大部分节点保持通信,就把自己降级为Secondary节点。
主节点选举触发的时机
- 第一次初始化一个复制集
- Secondary节点权重比Primary节点高时,发起替换选举
- Secondary节点发现集群中没有Primary时,发起选举
- Primary节点不能访问到大部分(Majority)成员时主动降级
当触发选举时,Secondary节点尝试将自身选举为Primary节点。主节点选举是一个二阶段+多数派协议。
第一阶段
检测自身是否有被选举的资格,如果符合资格会向其他节点发起本节点是否有选举资格的FreshnessCheck进行通辽仲裁。
第二阶段
发起者向集群中的存活节点发送Elect(选举)请求,仲裁者受到请求之后会进行一系列的合法检测,如果检测通过,则仲裁者为发送方节点投一票(一个复制集中最多有50个节点,其中只有7个具有投票权)。
pv0通过30秒选举锁来防止一次选举中两次投票。
pv1使用了terms(一个单调递增的选举计数器)来防止在一次选举中出现两次投票的情况。
多数派协议:
发起者如果获得了超过半数的投票,则选举通过,自身成为Primary节点。获得低于半数选票的原因,除了常见的网络问题之外,相同优先级的节点同时通过第一阶段的同僚仲裁并进入第二阶段也是一个原因。因此,当选票不足时候,会随机sleep[0, 1]秒后再次尝试选举。