图解 MongoDB 19|Oplog:复制的真正载体 不是文档是操作
上一篇文章解释了复制集的基本拓扑,但未涉及关键问题:Secondary如何同步Primary的数据?答案是Secondary主动拉取操作日志(oplog),而非Primary推送文档。本文将深入解析oplog这一复制核心载体。
实际上,Secondary拉取的是操作日志(oplog)本身,而非文档。理解oplog是掌握复制延迟、全量初始化及Secondary掉队等概念的关键。本文将详细阐述oplog作为复制载体的工作机制。
先把机制边界说清楚
Oplog(操作日志)是Primary上名为local.oplog.rs的特殊集合,记录每个写操作。Secondary通过长轮询(long polling / tailing)从Primary拉取oplog,并在本地重放以实现数据同步。
复制链路的完整形态如下:
- Primary接收写操作,先更新本地数据(集合与日志)。
- Primary将该操作追加至oplog。
- Secondary通过长轮询获取新的oplog记录。
- Secondary在本地重放该操作,同步数据。
- Secondary记录已应用的oplog位点(时间戳)。
复制基于异步操作日志,Secondary并非Primary的实时镜像,而是持续追赶oplog,因而天然存在延迟——下篇将专门分析复制延迟。
Oplog 是固定大小的环形缓冲


Oplog是一个capped collection(固定大小集合),默认大小根据WiredTiger引擎取空闲磁盘的5%(下限990MB,上限50GB)。它采用环形缓冲机制:写满后,最旧记录会被新记录覆盖。
该设计的优势是oplog不会无限膨胀,但带来一个重要后果:若Secondary落后过多,所需oplog已被覆盖,则无法通过拉取oplog追赶,必须进行全量重新初始化——从Primary完整拷贝数据后再追oplog。全量初始化开销极大,尤其在大集合上耗时显著。
因此,oplog大小需与写入速率匹配:写入越快、Secondary可能离线越久,oplog应越大。通过监控oplog的“时间窗口”(最早与最新记录的时间跨度),可判断Secondary能容忍多长时间的离线。
Oplog 记录的是操作,且必须幂等
每条oplog记录对应一个操作,结构大致如下:
复制代码{
ts: Timestamp, // 操作时间戳,单调递增
op: "i" | "u" | "d", // insert / update / delete
ns: "db.collection", // 命名空间
o: { ... } // 操作内容
}
幂等性是oplog设计的关键,即同一条操作重放多次结果与重放一次相同。这对复制至关重要,因为Secondary在网络抖动、重试或崩溃恢复时可能重复重放同一oplog。
为保证幂等,oplog记录update操作时会转为绝对值更新而非相对更新。例如应用执行{$inc: {count: 1}}(count加1),oplog不是记录“count加1”,而是计算更新后的绝对值,记录为“count设为X”。这样Secondary重放时无论次数,结果始终正确。
此设计牺牲了一定灵活性(需记录完整新值),但换来了复制的可靠性。理解了幂等性,就明白了复制为何可靠且不惧重试。
全量初始化:新节点的第一份完整数据
全新Secondary加入复制集时本地无数据,仅靠拉取oplog不可行(oplog只记录增量)。它需要全量初始化(initial sync):
- 从Primary(或其他Secondary)完整拷贝所有集合和索引。
- 拷贝期间记录产生的增量oplog。
- 拷贝完成后,重放积累的oplog以追至最新状态。
- 开始正常的长轮询复制。
全量初始化在大集合上非常缓慢(需拷贝全部数据),且会增加Primary负载。因此加节点应错峰进行,也可从已有Secondary拷贝以分摊负载。
Oplog 的几个反直觉点
oplog占用不计入业务数据。它位于local数据库,独立于业务数据,但仍占用磁盘空间,容量规划需将其纳入。
oplog大小可在线调整。通过replSetResizeOplog命令可在不重启情况下扩大或缩小oplog(下限990MB)。初期未估准时不必重做部署,但调整仍属运维操作,建议一次性规划到位。
oplog的写入也是写入开销。Primary每次写操作不仅要修改集合,还需追加oplog,因此复制集Primary的写入开销比单机更大。
Secondary延迟的本质是oplog追赶不及。下一章将详细展开,但根源在于oplog产生速度与Secondary重放速度之间的差距。
判断框架
- 复制传递的是操作(oplog),而非文档;Secondary主动拉取并重放,而非Primary推送。
- oplog是固定大小的环形缓冲,写满覆盖旧记录;落后过多会掉队并需全量初始化。
- oplog必须幂等,因此update记录绝对值,确保重放安全。
- 新节点需全量初始化(拷全量+追增量),大集合缓慢,应错峰添加。
- oplog大小需匹配写入速率,监控“时间窗口”以判断可容忍离线时长。
- oplog追加是Primary写开销的一部分,复制集写入比单机成本更高。
综上所述,复制的本质是Secondary异步拉取并重放Primary的oplog;oplog作为固定大小环形缓冲,以幂等方式记录操作;新节点需全量初始化;oplog大小需匹配写入速率以容忍离线。理解这些机制,就掌握了复制延迟的根源。下一篇将重点分析Secondary为何会落后于Primary。