阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

MongoDB复制集原理

168次阅读
没有评论

共计 4646 个字符,预计需要花费 12 分钟才能阅读完成。

复制集简介

Mongodb 复制集由一组 Mongod 实例(进程)组成,包含一个 Primary 节点和多个 Secondary 节点,Mongodb Driver(客户端)的所有数据都写入 Primary,Secondary 从 Primary 同步写入的数据,以保持复制集内所有成员存储相同的数据集,提供数据的高可用。

下图(图片源于 Mongodb 官方文档)是一个典型的 Mongdb 复制集,包含一个 Primary 节点和 2 个 Secondary 节点。

MongoDB 复制集原理

Primary 选举

复制集通过 replSetInitiate 命令(或 mongo shell 的 rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起 Priamry 选举操作,获得『大多数』成员投票支持的节点,会成为 Primary,其余节点成为 Secondary。

初始化复制集

config = {_id : "my_replica_set",
    members : [{_id : 0, host : "rs1.example.net:27017"},
         {_id : 1, host : "rs2.example.net:27017"},
         {_id : 2, host : "rs3.example.net:27017"},
   ]
}

rs.initiate(config)

『大多数』的定义

假设复制集内投票成员(后续介绍)数量为 N,则大多数为 N/2 + 1,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出 Primary,复制集将无法提供写服务,处于只读状态。

投票成员数 大多数 容忍失效数
1 1 0
2 2 0
3 2 1
4 3 1
5 3 2
6 4 2
7 4 3

通常建议将复制集成员数量设置为奇数,从上表可以看出 3 个节点和 4 个节点的复制集都只能容忍 1 个节点失效,从『服务可用性』的角度看,其效果是一样的。(但无疑 4 个节点能提供更可靠的数据存储)

特殊的 Secondary

正常情况下,复制集的 Seconary 会参与 Primary 选举(自身也可能会被选为 Primary),并从 Primary 同步最新写入的数据,以保证与 Primary 存储相同的数据。

Secondary 可以提供读服务,增加 Secondary 节点可以提供复制集的读服务能力,同时提升复制集的可用性。另外,Mongodb 支持对复制集的 Secondary 节点进行灵活的配置,以适应多种场景的需求。

Arbiter

Arbiter 节点只参与投票,不能被选为 Primary,并且不从 Primary 同步数据。

比如你部署了一个 2 个节点的复制集,1 个 Primary,1 个 Secondary,任意节点宕机,复制集将不能提供服务了(无法选出 Primary),这时可以给复制集添加一个 Arbiter 节点,即使有节点宕机,仍能选出 Primary。

Arbiter 本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入一个 Arbiter 节点,以提升复制集可用性。

Priority0

Priority0 节点的选举优先级为 0,不会被选举为 Primary

比如你跨机房 A、B 部署了一个复制集,并且想指定 Primary 必须在 A 机房,这时可以将 B 机房的复制集成员 Priority 设置为 0,这样 Primary 就一定会是 A 机房的成员。(注意:如果这样部署,最好将『大多数』节点部署在 A 机房,否则网络分区时可能无法选出 Primary)

Vote0

Mongodb 3.0 里,复制集成员最多 50 个,参与 Primary 选举投票的成员最多 7 个,其他成员(Vote0)的 vote 属性必须设置为 0,即不参与投票。

Hidden

Hidden 节点不能被选为主(Priority 为 0),并且对 Driver 不可见。

因 Hidden 节点不会接受 Driver 的请求,可使用 Hidden 节点做一些数据备份、离线计算的任务,不会影响复制集的服务。

Delayed

Delayed 节点必须是 Hidden 节点,并且其数据落后与 Primary 一段时间(可配置,比如 1 个小时)。

因 Delayed 节点的数据比 Primary 落后一段时间,当错误或者无效的数据写入 Primary 时,可通过 Delayed 节点的数据来恢复到之前的时间点。

数据同步

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" : Timestamp(1446011584, 2),
  "h" : NumberLong("1687359108795812092"), 
  "v" : 2, 
  "op" : "i", 
  "ns" : "test.nosql", 
  "o" : {"_id" : ObjectId("563062c0b085733f34ab4129"), "name" : "mongodb", "score" : "100" } 
}
ts:操作时间,当前 timestamp + 计数器,计数器每秒都被重置
h:操作的全局唯一标识
v:oplog 版本信息
op:操作类型
i:插入操作
u:更新操作
d:删除操作
c:执行命令(如 createDatabase,dropDatabase)n:空操作,特殊用途
ns:操作针对的集合
o:操作内容,如果是更新操作
o2:操作查询条件,仅 update 操作包含该字段

Secondary 初次同步数据时,会先进行 init sync,从 Primary(或其他数据更新的 Secondary)同步全量数据,然后不断通过 tailable cursor 从 Primary 的 local.oplog.rs 集合里查询最新的 oplog 并应用到自身。

init sync 过程包含如下步骤

T1 时间,从 Primary 同步所有数据库的数据(local 除外),通过 listDatabases + listCollections + cloneCollection 敏命令组合完成,假设 T2 时间完成所有操作。
从 Primary 应用 [T1-T2] 时间段内的所有 oplog,可能部分操作已经包含在步骤 1,但由于 oplog 的幂等性,可重复应用。
根据 Primary 各集合的 index 设置,在 Secondary 上为相应集合创建 index。(每个集合_id 的 index 已在步骤 1 中完成)。
oplog 集合的大小应根据 DB 规模及应用写入需求合理配置,配置得太大,会造成存储空间的浪费;配置得太小,可能造成 Secondary 的 init sync 一直无法成功。比如在步骤 1 里由于 DB 数据太多、并且 oplog 配置太小,导致 oplog 不足以存储 [T1, T2] 时间内的所有 oplog,这就 Secondary 无法从 Primary 上同步完整的数据集。

修改复制集配置

当需要修改复制集时,比如增加成员、删除成员、或者修改成员配置(如 priorty、vote、hidden、delayed 等属性),可通过 replSetReconfig 命令(rs.reconfig())对复制集进行重新配置。

比如将复制集的第 2 个成员 Priority 设置为 2,可执行如下命令

cfg = rs.conf();
cfg.members[1].priority = 2;
rs.reconfig(cfg);

细说 Primary 选举

Primary 选举除了在复制集初始化时发生,还有如下场景

复制集被 reconfig
Secondary 节点检测到 Primary 宕机时,会触发新 Primary 的选举
当有 Primary 节点主动 stepDown(主动降级为 Secondary)时,也会触发新的 Primary 选举
Primary 的选举受节点间心跳、优先级、最新的 oplog 时间等多种因素影响。

节点间心跳

复制集成员间默认每 2s 会发送一次心跳信息,如果 10s 未收到某个节点的心跳,则认为该节点已宕机;如果宕机的节点为 Primary,Secondary(前提是可被选为 Primary)会发起新的 Primary 选举。

节点优先级

每个节点都倾向于投票给优先级最高的节点
优先级为 0 的节点不会主动发起 Primary 选举
当 Primary 发现有优先级更高 Secondary,并且该 Secondary 的数据落后在 10s 内,则 Primary 会主动降级,让优先级更高的 Secondary 有成为 Primary 的机会。

Optime

拥有最新 optime(最近一条 oplog 的时间戳)的节点才能被选为主。

网络分区

只有更大多数投票节点间保持网络连通,才有机会被选 Primary;如果 Primary 与大多数的节点断开连接,Primary 会主动降级为 Secondary。当发生网络分区时,可能在短时间内出现多个 Primary,故 Driver 在写入时,最好设置『大多数成功』的策略,这样即使出现多个 Primary,也只有一个 Primary 能成功写入大多数。

复制集的读写设置

Read Preference

默认情况下,复制集的所有读请求都发到 Primary,Driver 可通过设置 Read Preference 来将读请求路由到其他的节点。

primary:默认规则,所有读请求发到 Primary
primaryPreferred:Primary 优先,如果 Primary 不可达,请求 Secondary
secondary:所有的读请求都发到 secondary
secondaryPreferred:Secondary 优先,当所有 Secondary 不可达时,请求 Primary
nearest:读请求发送到最近的可达节点上(通过 ping 探测得出最近的节点)

Write Concern

默认情况下,Primary 完成写操作即返回,Driver 可通过设置 [Write Concern(https://docs.mongodb.org/manual/core/write-concern/) 来设置写成功的规则。

如下的 write concern 规则设置写必须在大多数节点上成功,超时时间为 5s。

db.products.insert({ item: "envelopes", qty : 100, type: "Clasp" },
  {writeConcern: {w: majority, wtimeout: 5000 } }
)

上面的设置方式是针对单个请求的,也可以修改副本集默认的 write concern,这样就不用每个请求单独设置。

cfg = rs.conf()
cfg.settings = {}
cfg.settings.getLastErrorDefaults = {w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)

异常处理(rollback)

当 Primary 宕机时,如果有数据未同步到 Secondary,当 Primary 重新加入时,如果新的 Primary 上已经发生了写操作,则旧 Primary 需要回滚部分操作,以保证数据集与新的 Primary 一致。

旧 Primary 将回滚的数据写到单独的 rollback 目录下,数据库管理员可根据需要使用 mongorestore 进行恢复。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-09/146670.htm

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计4646字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中