i、u、d 分别对应 insert、update、delete 操作:i 的 o 是完整文档,u 的 o 是更新器且需结合原文档计算,d 的 o 是删除条件;oplog 不提供快照,无事务边界,格式随版本变化,云服务常限制访问。

Oplog 里的 i、u、d 分别对应什么操作
它们是 MongoDB 副本集 oplog(operations log)中记录变更的最简操作类型标识,不是命令也不是状态,而是服务端写入 oplog 时自动打上的动作标签:
-
i:insert,表示一条新文档被插入到集合中;oplog 条目里o字段是完整文档内容 -
u:update,表示已有文档被修改;o是更新器(如{$set: {...}}),o2是匹配条件(通常是{_id: ...}) -
d:delete,表示文档被删除;o是删除条件(如{_id: ObjectId("...")}),没有o2
注意:u 和 d 都不记录原始文档快照,只记“怎么改”或“删谁”,这是做增量同步时容易漏掉数据的关键点。
为什么不能直接靠 o 字段还原出变更后的文档
因为 o 在不同操作类型下语义完全不同,硬解析会出错:
-
i的o是完整文档 → 可直接用 -
u的o是更新操作符(如{$set: {a: 1}, $inc: {b: 2}})→ 必须结合原文档计算结果 -
d的o是查询条件 → 不含任何字段值,无法还原被删内容
常见错误:写监听脚本时统一取 o 当作新文档,结果 u 和 d 场景下拿到的是垃圾数据。真实场景中,要做字段级变更同步,必须搭配上游数据库的读取能力(比如用 find 查原文档再 apply update)。
从 oplog 里安全读取变更的几个硬约束
oplog 不是通用日志接口,它设计目标是副本集内部复制,对外暴露有严格限制:
- oplog 是 capped collection,空间固定,旧条目会被覆盖 → 不能长期离线消费,必须保持游标活跃
- oplog 没有事务边界,多文档更新(如
updateMany)会拆成多个u条目,但彼此无顺序标记 → 无法保证原子性还原 - 4.2+ 版本开始支持
applyOps命令回放,但仅限 admin 数据库且需权限;普通应用不可直接调用 - 使用
getMore拉取 oplog 时,若游标超时(默认 10 分钟),下次必须从最新位置重连,中间断档不可恢复
也就是说,想靠 tail oplog 做准实时同步,得自己处理断连、去重、幂等、反向查原值——这些都不是 MongoDB 帮你兜底的事。
实际用 oplog 做同步时最容易忽略的兼容性坑
oplog 格式随版本演进过多次,低版本驱动或自定义解析器常在这里翻车:
- 3.6 之前:oplog 条目用
ts(Timestamp)字段做排序和游标,类型是Timestamp(非Date);3.6+ 开始加了t(term)字段协同判断主节点切换 - 4.0 引入
lsid和txnNumber,用于关联事务内的多条 oplog,但仅当操作在显式事务中才出现 → 单条insert不会有这些字段 - 某些云托管服务(如 Atlas)默认隐藏 oplog,或只开放只读副本节点的 oplog 访问,且可能禁用
local.oplog.rs的 direct read 权限
最典型的失败场景:用 Node.js 驱动连 Atlas,代码里写 db.collection('local.oplog.rs').find(...),结果抛 NamespaceNotFound 或权限错误——不是语法问题,是服务端根本没开这个入口。










