必须显式启用 changestreampreandpostimages 才能获取 update/replace 前后文档,否则 fulldocumentbeforechange 为空、fulldocument 在非 insert 场景下可能为 null;该配置需集合级开启,且仅对新写入生效。

必须显式启用 changeStreamPreAndPostImages 才能拿到 update/replace 前后文档,否则 fullDocumentBeforeChange 和部分 fullDocument 字段为空 —— 这是 MongoDB 6.0 最常踩的坑。
开启 pre/post images 是使用变更流的前提
MongoDB 6.0 默认不存储文档变更前后的快照。即使你监听了 update 事件,fullDocumentBeforeChange 字段也始终缺失,fullDocument 在非 insert 场景下也可能为 null。这不是驱动或代码问题,而是集合级配置未打开。
- 创建集合时加选项:
db.createCollection("orders", { changeStreamPreAndPostImages: { enabled: true } }) - 对已有集合启用:
db.runCommand({ collMod: "orders", changeStreamPreAndPostImages: { enabled: true } }) - 该配置仅对新写入生效,历史文档无前后像
- 启用后,
fullDocument在update、replace、delete事件中才稳定可用(delete下为被删前的完整文档)
watch() 聚合管道里只能用 $match 筛操作类型
db.collection.watch() 的 pipeline 参数不是万能过滤器。它只支持有限聚合阶段,且不能修改 _id 字段(否则直接抛异常)。实际生产中,你几乎只会用 $match 来聚焦特定操作。
- 监听 insert + update:
[{ $match: { operationType: { $in: ["insert", "update"] } } }] - 排除 delete:
[{ $match: { operationType: { $ne: "delete" } } }] - 匹配特定字段更新:
[{ $match: { "updateDescription.updatedFields.price": { $exists: true } } }] - 不要在管道里用
$project或$addFields重写_id,会触发IllegalOperation
resumeToken 断点续传必须手动保存和恢复
变更流不是“启动就一直跑”的黑盒。网络抖动、primary 切换、JVM 重启都会中断游标。MongoDB 不自动记住位置,靠你存 _id 中的 _data 字段值(即 resumeToken)来恢复。
- 每次成功处理事件后,应持久化当前事件的
_id值(例如写入 Redis 或本地文件) - 重启监听器时,从存储中读出上一次的
resumeToken,传给watch()的options.resumeAfter - 别用
startAfter:它跳过指定 token 对应事件,可能丢数据;resumeAfter是“从此之后开始”,语义安全 - 首次运行无 token 时,传
null或不设该字段,从当前最新位置开始(注意:可能漏掉启动瞬间的变更)
真正难的不是写几行 watch(),而是让 resumeAfter 在集群故障、服务滚动发布、日志堆积等场景下依然指向正确位置。token 本身是 BinData,不同版本编码方式还可能变,别硬解析,原样存取即可。











