
作者:赵师的工作日(赵明中)
现役Oracle ACE、MySQL 8.0 ocp、TiDB PCTA\PCTP、Elasticsearch Certified Engineer
微信公众号:赵师的工作日
CSND:赵师的工作日
一、MongoDB复制概述
MongoDB的复制机制基于主从结构,提供了高可用性和数据冗余。MongoDB的复制系统由一个主节点(Primary)和多个从节点(Secondary)组成,主节点处理写操作,并将这些操作通过复制传送到从节点。从节点同步主节点的数据,确保它们的数据一致性。
复制集(Replica Set)
在MongoDB中,多个节点(通常是三节点或五节点)通过复制集组成一个集群。每个复制集包含以下几种角色:
- Primary:主节点,处理所有的写操作。
- Secondary:从节点,从主节点获取数据并同步更新。它们可以充当备份节点,提供容灾能力。
- Arbiter:仲裁节点,它不会存储数据,只参与选举操作。通常用于提供奇数节点以避免在选举时发生投票平局的情况。
MongoDB复制集提供了以下特点:
- 数据冗余
- 高可用性
- 数据自动恢复
二、实时复制的工作原理
在MongoDB的复制机制中,主节点和从节点之间的同步是实时的。实时复制的关键是通过oplog(操作日志)来实现的。
Oplog(操作日志)
Oplog是MongoDB复制的核心,它是一个特殊的集合(local.oplog.rs),用于记录所有的写操作。这些写操作包含增、删、改等操作。主节点会将所有操作记录到Oplog中,从节点会不断地读取这个日志并同步数据。
Oplog的工作流程:
(1)主节点接收到写操作请求并执行操作。
(2)主节点将写操作记录到Oplog中。
(3)从节点会周期性地从主节点的Oplog中拉取最新的操作,并在本地执行相同的写操作,以保持数据同步。
实时复制的同步流程:
(1)写操作到达主节点: 写操作首先到达主节点,主节点执行该操作后,将其记录到oplog中。
(2)从节点拉取Oplog: 从节点不断监视主节点的Oplog,并从中拉取新的写操作(如插入、更新、删除等)。
(3)同步数据: 从节点根据Oplog中的操作记录,按照相同顺序执行操作,以保持数据与主节点一致。
(4)延迟处理: 主节点和从节点之间的数据同步通常是实时的,但由于网络延迟、磁盘写入等因素,可能会出现微小的延迟。
三、数据同步的机制:拉模式 vs 推模式
MongoDB的复制采用拉模式来同步数据,而不是推模式。
拉模式
- 从节点主动连接主节点拉取Oplog中的操作记录。
- 从节点按顺序执行这些操作,保证与主节点数据一致。
这种拉模式的优点是从节点可以根据自己的进度来拉取数据,不必等待主节点主动推送,从而减少了网络负担。
四、数据一致性与复制延迟
尽管MongoDB提供了实时复制机制,但由于复制是异步的,因此可能存在一定的延迟。即从节点可能会落后于主节点,无法立即看到主节点上的所有数据更改。
一致性模型
MongoDB提供了多种一致性级别,用户可以根据需要选择适当的一致性保证:
- 节点级别的写入一致性(Write Concern):指定写操作需要多少节点确认才能认为写操作成功。
- 读取一致性(Read Concern):指定读取操作从哪些节点读取数据,并保证读取数据的一致性。
在MongoDB 4.4开始,提供了更多的选项来平衡性能和一致性需求。通过majority写入关心和linearizable读取关心,用户可以确保在分布式环境下的数据一致性。
五、实时复制中的故障恢复
在MongoDB复制集中,如果主节点发生故障,MongoDB会启动一个自动选举机制,通过仲裁节点或者从节点的投票选举出一个新的主节点。这一过程是自动的,确保复制集能够继续提供服务。
(1)主节点故障: 如果主节点故障,复制集中的从节点将进入选举状态,并通过多数节点选举出新的主节点。
(2)自动恢复: 新的主节点会接管写操作,并通过Oplog继续同步数据。故障发生时,MongoDB尽量确保最小的停机时间。
六、代码层面:实时复制的实现
在MongoDB的源代码中,实时复制的实现主要集中在以下几个模块:
- Replication:负责协调复制集中的各个节点,处理Oplog日志、复制流、故障转移等功能。
- Oplog:MongoDB的每个写操作都在oplog中记录,Oplog的操作日志是复制的核心。
- Sync:负责从节点拉取Oplog并同步数据。
具体到MongoDB 4.x以上源码中,Oplog的相关操作可以在replication模块中找到。Oplog的相关实现涉及到OplogWriter(写操作)和OplogReader(读操作)等组件。
类源代码实现: - Oplog日志的写入:
cpp
Status ReplicationCoordinatorImpl::logOp(OperationContext* opCtx,
const BSONObj& op) {
auto oplogEntry = createOplogEntry(op);
try {
writeToOplog(opCtx, oplogEntry);
} catch (const DBException& e) {
return e.toStatus();
}
return Status::OK();
}
- 从节点拉取Oplog:
cpp
void ReplicationExecutor::processOplogEntries() {
while (true) {
// 拉取新的Oplog数据
auto oplog = oplogFetcher.fetchNextOplogEntry();
if (oplog) {
applyOplog(oplog);
}
}
}
这些代码片段展示了如何处理Oplog的写入和从节点如何拉取Oplog并同步数据。
七、性能优化与挑战
尽管MongoDB提供了实时复制的机制,但在大规模生产环境中,仍然需要关注一些性能优化点:
- 网络带宽: 高写入压力下,复制过程可能导致网络带宽的消耗,需要保证足够的网络资源。
- Oplog大小: 如果Oplog日志过大,可能会影响性能。因此,合理的Oplog大小和清理机制至关重要。
- 复制延迟: 在高负载情况下,可能会出现一定的复制延迟。可以通过监控工具(如MongoDB Atlas或rs.printSlaveReplicationInfo())来实时查看延迟情况。





