关注我,可以获取最新知识、经典面试题以及微服务技术分享
MongoDB 复制集(replica set):MongoDB 复制集维护相同数据集的一组 mongod 进程,复制集是生产部署的基础,具有数据冗余以及高可用性。
那为什么要设置复制集 (replica set) 呢?
- 由于复制集是通过在不同服务器上保存来副本,可保证数据在生产部署的冗余和可靠性,不会因为单点问题而丢失数据。
- 可以通过访问不同服务器副本数据来提高数据读取能力,从而提高整个系统的负载能力。
1. 复制集架构原理
副本集包含 多个数据节点 和可选的一个仲裁节点 。而在数据节点中:只有一个 主节点 (primary node),其他节点为为 从节点(secondary nodes)。
各个节点成员通过心跳机制进行通信,当主节点与从节点的通信的时间超过配置的 electionTimeoutMillis 期间(默认为 10 秒)时,符合条件的从节点要求选举将自己指定为新主节点,群集尝试完成新主节点的选举并恢复正常操作。
主节点: 副本集只能有一个主节点能够确认写入操作来接收所有写操作,并记录其操作日志中的数据集的所有更改(记录在 oplog 中)。
oplog: 它保存了修改存储在数据库中的数据的所有操作的滚动记录,MongoDB 在主节点服务器上应用数据库操作,然后在主节点服务器的 oplog 上记录操作,然后从节点成员在异步过程中通过心跳机制从任何其他成员导入 oplog 并应用这些操作,oplog 中的每个操作都是幂等的。所有副本集成员都在 local.oplog.rs 集合中包含 oplog 的副本,这允许它们维护数据库的当前状态。
从节点: 复制主节点的 oplog 并将 oplog 记录的操作应用于其数据集,如果主节点宕机了,将从符合条件的从节点选举选出新的主节点,。而且你可以通过配置实现特定的功能,比如:
- 防止从节点成为选举中的主节点 Primary,指定节点优先级。
- 阻止应用程序从节点读取数据,从而允许应用程序运行需要与正常流量分离的应用程序,隐藏节点。
- 保留正在运行的“历史”快照,以用于从某些错误中恢复,例如无意中删除的数据库,延迟节点
仲裁节点: 仲裁节点不维护数据集。仲裁节点的目的是通过响应其他副本集节点的心跳和选举请求来维护副本集中的仲裁。因为它们不存储数据集,所以仲裁节点可以是提供副本集仲裁功能的好方法,其资源成本比具有数据集的全功能副本集成员更便宜。如果您的副本集具有偶数个成员,请添加仲裁节点以获得主要选举中的大多数投票。而且仲裁节点总是只有 1 次选举投票,因此允许副本集具有不均匀的投票成员数,而没有复制数据的额外成员的开销。
心跳机制(Hearbeat): 复制集成员间默认每 2s 会发送一次心跳信息,如果 10s 未收到某个节点的心跳,则认为该节点已宕机不可以访问;如果宕机的节点为 Primary,Secondary(前提是可被选为 Primary)会发起新的 Primary 选举。仲裁员与其他集合成员之间的唯一沟通是:选举期间的投票,心跳和配置数据,而且这些交换未加密。
数据同步: 为了维护共享数据集的最新副本,副本的从节点设置同步或复制来自其他节点的数据。MongoDB 使用两种形式的数据同步:初始化同步新节点同步完整的数据集,以及整个集群节点同步后续数据更改。
其中,初始化同步(Initial Sync)过程:
- 克隆除本地数据库之外的所有数据库。要进行克隆,mongod 会扫描每个源数据库中的每个集合,并将所有数据插入到这些集合的自己的副本中。
初始同步会在为每个集合复制文档时构建所有集合索引。在早期版本的 MongoDB 中,在此阶段仅构建_id 索引。
- 初始同步在数据复制期间提取新添加的 oplog 记录。确保目标成员在本地数据库中有足够的磁盘空间,以便在此数据复制阶段的持续时间内临时存储这些 oplog 记录。
- 将所有更改应用于数据集。使用来自源的 oplog,mongod 更新其数据集以反映副本集的当前状态。
初始同步完成后,成员从 STARTUP2 转换为 SECONDARY。
2. 标准复制集架构
标准复制集架构由三台服务器,其中包括三个数据节点 (一个主节点、两个从节点) 或两个数据节点 (一个主节点、一个从节点) 和一个仲裁节点两种情况。如下所示:
三个数据节点:
- 一个主节点;
- 两个从节点,主节点宕机后,有机会选举成为主节点。
当主库宕机后, 两个从库都会进行竞选,其中一个变为主库,当原主库恢复后,作为从库加入当前的复制集群即可。
两个数据节点以及一个仲裁节点:
- 一个主节点;
- 一个从节点,有机会被选举成为主节点;
- 一个仲裁节点,只有投票权利。
当主节点不可用时,将会选择从节点成为主 Primary,主节点恢复后,将其作为从节点加入到现有的复制集群中即可。
3. 节点类型
优先级 0 型 (Priority 0) 节点
优先级 0 型节点不可以成为成为主节点,也不能触发选举。将从节点配置为优先级为 0 以防止它成为主节点,这在多数据中心部署中特别有用,在许多情况下,您无需将备用数据库设置为优先级 0. 但是,在具有不同硬件或地理分布的副本集中,优先级为 0 的备用数据库可确保仅某些成员成为主数据库,这样可以根据实际网络分区的网络质量等实际情况进行配置。
例如,一个数据中心承载主数据中心和辅助数据中心:
将第二个数据中心节点优先级为 0 只能为从节点数据库,而数据中心 1 中的节点才能成为主节点数据库。(比如你跨机房 A、B 部署了一个复制集,并且想指定 Primary 必须在 A 机房,这时可以将 B 机房的复制集成员 Priority 设置为 0,这样 Primary 就一定会是 A 机房的成员),
隐藏型 (Hidden) 节点
隐藏型 (Hidden) 节点:
- 隐藏型从节点是维护主数据集的副本,但对客户端应用程序不可见。隐藏型从节点适用于具有与副本集中其他成员不同的使用模式。
- 隐藏型从节点必须始终优先为 0 型从节点,因此不能成为主节点。隐藏型从节点可能会在选举中投票。
- 隐藏型从节点将不会收到来自应用程序的请求。我们可以将隐藏型从节点专用于报表节点或是备份节点。
延迟型 (Delayed) 节点
由于延迟型从节点是数据集的“滚动备份”或运行“历史”快照,因此它们可以帮助您从各种人为错误中恢复。例如,延迟节点可以从不成功的应用程序升级和操作员错误(包括丢弃的数据库和集合)中恢复。而且延迟型从节点一定是优先级为 0 的从节点,也是隐藏型从节点。不能成主节点,也不能给客户端查询。
在选择延迟量时,请考虑延迟量:
- 必须等于或大于预期的维护窗口持续时间。
- 必须小于 oplog 的容量。
投票型 (Vote) 节点以及不可投票节点
复制集节点可以通过配置 members[n].votes 来决定该节点是否具有投票权利!members[n].votes 值为 1 具有投票权利为投票型节点,为 0 则不可以投票即为不可投票节点。无表决权的节点必须优先级为 0,也是优先级大于 0 的成员不能为 0 值。虽然无表决权的成员不在选举中投票,但这些成员持有副本集数据的副本,并且可以接受来自客户端应用程序的读取操作。
另外在副本集最多可包含 50 个成员,但只有 7 个投票成员,因此非投票成员允许副本集具有 7 个以上的成员。并投票成员只有具备以下状态可以进行投票:
- PRIMARY
- SECONDARY
- STARTUP2
- RECOVERING
- ARBITER
- ROLLBACK
配置:
{
"_id" : <num>,
"host" : <hostname:port>,
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 0
}
4. 部署结构:
最大投票成员为数量
副本集最多可包含 50 个成员,但只有 7 个投票成员。如果副本集已有 7 个投票成员,则其他成员必须是非投票成员。
部署奇数个成员
副本集应该确保具有奇数个投票成员,如果您拥有偶数个投票成员,请部署仲裁节点,以便该集合具有奇数个投票成员。仲裁节点不存储数据的副本并且需要更少的资源。因此,您可以在应用程序服务器或其他共享进程上运行仲裁程序。
容错能力
副本集的容错是当变为不可用的成员数,并且仍然在副本集中留下足够的节点成员来选择主节点成员。容错是副本集大小的影响,见下表:
Number of Members | Majority Required to Elect a New Primary | Fault Tolerance |
---|---|---|
3 | 2 | 1 |
4 | 3 | 1 |
5 | 3 | 2 |
6 | 4 | 2 |
因此可以得出,将成员添加为偶数个到副本集并不总是会增加容错能力。但是,在这些情况下,其中将其中一个节点设置成隐藏型和延迟型从节点可以为专用功能提供支持,例如备份或报告。
提高读负载能力
在具有非常高读取流量的部署中,您可以通过将读取分发给从节点来提高读取吞吐量。随着部署的增长,将节点添加或移动到备用数据中心以提高冗余和可用性。
副本集分布在两个或更多数据中心
副本集分布在两个或更多数据中心的优势:
- 如果其中一个数据中心发生故障,数据仍可用于读取。
- 如果具有少数成员的数据中心发生故障,则副本集仍可以提供写操作以及读操作。但是,如果具有大多数成员的数据中心发生故障,则副本集将变为只读。
在不同地域部署数据节点(具有备用的数据中心)
要在数据中心发生故障时保护您的数据,请在备用数据中心至少保留一个成员。如果可能,使用奇数个数据中心,并选择一个成员分布,以最大限度地提高即使丢失数据中心的可能性,剩余的副本集成员可以形成可以形成“大多数”选取出主节点,并有提供数据的副本的能力。为确保主数据中心的节点在备用数据中心的成员之前被选为主要成员,请将备用数据中心中节点 members[n].priority 设置为低于主数据中节点,如下所示:
根据部署结构部署复制集示例
三个节点成员的副本集,成员合理分布以及解析如下:
- 两个数据中心:数据中心 1 的两个成员和数据中心 2 的一个成员。如果副本集的其中一个成员是仲裁者,则将仲裁者分配给具有数据承载成员的数据中心 1。
- 如果数据中心 1 关闭,则副本集将变为只读。
- 如果数据中心 2 关闭,则副本集仍然可写,因为数据中心 1 中的成员可以进行选举。
- 三个数据中心:一个成员到数据中心 1,一个成员到数据中心 2,一个成员到数据中心 3。
- 如果任何数据中心发生故障,副本集仍然可写,因为其余成员可以举行选举。
五副节点成员的副本集,成员合理分布以及解析如下:
- 两个数据中心:数据中心 1 的三个成员和数据中心 2 的两个成员。
- 如果数据中心 1 关闭,则副本集将变为只读。
- 如果数据中心 2 关闭,则副本集仍然可写,因为数据中心 1 中的成员可以创建多数。
- 三个数据中心:数据中心 1 的两个成员,数据中心 2 的两个成员和数据中心 3 的一个成员。
- 如果任何数据中心发生故障,副本集仍然可写,因为其余成员可以举行选举。
高可用
集群具有自主选举能力,影响选取的因子和条件有以下:
- 选取协议
- 心跳机制:复制集成员间默认每 2s 会发送一次心跳信息,如果 10s 未收到某个节点的心跳,则认为该节点已宕机不可以访问;如果宕机的节点为 Primary,Secondary(前提是可被选为 Primary)会发起新的 Primary 选举。
- 节点优先权:每个节点都会倾向于投票给优先级最高的节点,优先级为 0 的节点不能成为主节点 Primary,也不会主动发起 Primary 选举。当 Primary 发现有优先级更高 Secondary,并且该 Secondary 的数据落后在 10s 内,则 Primary 会主动降级,让优先级更高的 Secondary 有成为 Primary 的机会。
- 丢失数据中心:使用分布式副本集,数据中心的丢失可能会影响其他数据中心或数据中心中其余成员选择主数据库的能力。如果可能,在数据中心之间分发副本集成员,以最大限度地提高即使丢失数据中心的可能性,其余一个副本集成员也可以成为新的主要成员。
- 网络分区:只有跟大多数投票节点间能保持网络畅通,才有机会被选主节点 Primary;如果 Primary 与大多数的节点失去联系,Primary 会主动降级为 Secondary。当发生网络分区时,可能在短时间内出现多个 Primary,故 Driver 在写入时,最好设置『大多数成功』的策略,这样即使出现多个 Primary,也只有一个 Primary 能成功写入大多数。
5. Write concern 和 Read Preference
5.1 Write concern
Write concern 描述了在操作返回成功之前必须确认写操作的数据承载成员(即主节点成员和从节点成员,但不是仲裁者)的数量。成员只能在收到并成功应用写入后才能确认写入操作。
对于副本集,默认的 w:1 的 Write concern 要求在返回 Write concern 确认之前,只有 Primary 主节点确认写入。您可以指定一个大于 1 的整数值,以要求来自主节点的确认以及满足指定值所需的多个从节点,最多为副本集中数据承载成员的总数。
Client 发出带有需要写入请求的写入操作 Write concern 将等待直到主节点接收来自指定需要写入询问所有数量的成员的确认。对于大于 1 或 w:“majority”的写入咨询 Write concern,主节点接收到所需的从节点数量在返回确认可写入答复通知 client 确认写入。对于 w:1 的写入咨询 Write Concern,主要可以在本地应用 (单机模式) 写入时立即返回可写入答复,因为它有资格对所请求的 Write Concern 做出判决。
指定超时等待写入咨询 Write concern 的写操作仅表示所需数量的副本集成员未在 wtimeout 时间段内确认写操作。它不一定表示主节点 Primary 未能应用写入。
检验写操作
在 insert()方法中增加 write Concern 选项,并指定“大多数”写入关注和 5 秒超时,以便操作不会无限期地阻塞,如下:
db.products.insert({ item: "envelopes", qty : 100, type: "Clasp"},
{writeConcern: { w: "majority" , wtimeout: 5000} }
)
例如,在 3 个节点成员的副本集中,操作将需要来自 3 个成员中的 2 个的确认。如果稍后缩放副本集以包括两个额外的投票节点,则相同的操作将需要来自 5 个副本集成员中的 3 个的确认。如果主节点服务器未在 wtimeout 限制内返回写入咨询 Write concern 确认,则写入操作将失败并出现写入问题错误。
修改默认 Write Concern
可以通过在副本集配置中设置 settings.getLastErrorDefaults 设置来修改副本集的默认写入问题。配置在返回之前等待写操作(在大多数投票成员上确认后)操作命令:
cfg = rs.conf()
cfg.settings.getLastErrorDefaults = {w: "majority", wtimeout: 5000}
rs.reconfig(cfg)
5.2 Read Preference
Read Preference 是 mongodb 如何将读操作分配到节点中,默认情况下,应用程序将其读取操作定向到副本集中的主要成员(即读取首选项模式“primary”)。但是,客户端可以指定读取首选项以将读取操作发送到辅助节点。Read Preference 模式如下:
- primary:默认规则,所有读请求发到 Primary
- primaryPreferred:Primary 优先,如果 Primary 不可达,请求 Secondary
- secondary:所有的读请求都发到 secondary
- secondaryPreferred:Secondary 优先,当所有 Secondary 不可达时,请求 Primary
- nearest:读请求发送到最近的可达节点上(通过 ping 探测得出最近的节点)
以下是使用读取首选项模式的常见用例:
- 为地理分布的应用程序提供本地读取。
- 如果您在多个数据中心中安装了应用程序服务器,则可以考虑使用地理位置分散的副本集并使用非主要或最近的读取首选项。这允许客户端从最低延迟成员读取,而不是始终从主要成员读取。
- 在故障转移期间维护可用性。
- 如果希望应用程序在正常情况下从主数据库读取,则允许使用 primaryPreferred,但在主数据库不可用时允许从辅助服务器读取过时的数据。这为故障转移期间的应用程序提供了“只读模式”。
最后可关注公众号,一起学习, 每天会分享干货,还有学习视频领取!