mongodb跨集合事务必须满足副本集或分片集群、版本≥4.0(副本集)或≥4.2(分片)、集合非capped等硬性前提,且所有crud操作须显式传入同一session,跨库操作需手动获取db引用并统一传session。

MongoDB 事务能完成跨集合 CRUD,但必须满足几个硬性前提:副本集或分片集群、4.0+(副本集)或 4.2+(分片)、所有涉及集合不能是 capped 类型。不满足任一条件,session.startTransaction() 会直接报错或静默降级为非事务执行。
事务启动前必须检查的三件事
很多跨集合操作失败,不是代码写错了,而是环境没达标。
- 运行
rs.status()或sh.status()确认部署类型 —— 单机版 MongoDB 不支持任何事务 - 确认所有目标集合都已存在,且不是
capped: true—— 事务中无法隐式创建capped集合,也无法对它执行readConcern: "snapshot" - 检查连接字符串是否包含
?replicaSet=...(副本集)或已正确配置分片路由 —— 缺少 replicaSet 会导致事务 session 无法绑定到主节点
db.collection.updateOne() 在事务中要加 session 参数
事务内所有 CRUD 操作都必须显式传入 session 对象,否则操作脱离事务上下文,变成独立提交的“裸写”。这是最常被忽略的坑。
- 错误写法:
db.orders.updateOne({ _id: 1 }, { $set: { status: "shipped" } })—— 这个更新不在事务里 - 正确写法:
db.orders.updateOne({ _id: 1 }, { $set: { status: "shipped" } }, { session }) - 同理,
insertOne()、deleteMany()、find()(带readConcern: "snapshot")也都必须带session
跨数据库事务需要显式指定 db 对象
MongoDB 允许事务跨越不同数据库,但驱动层不会自动帮你切换 db 上下文。你得手动拿到每个库的引用,再调用其集合方法。
- 不能写:
db.inventory.updateOne(...)和db.logs.insertOne(...)—— 这两个db是同一个默认数据库实例 - 必须写:
db.getSiblingDB("inventory").orders.updateOne(..., { session })和db.getSiblingDB("logs").events.insertOne(..., { session }) - Node.js 驱动中对应的是
client.db("inventory").collection("orders")和client.db("logs").collection("events"),两者都传同一session
事务真正难的不是语法,而是 write concern 和 read concern 的组合选择 —— 比如 writeConcern: { w: "majority" } 加 readConcern: "snapshot" 能保证强一致性,但会显著拖慢吞吐;而用 w: 1 虽快,却可能在故障时丢失已确认的写入。这个权衡点往往被开发阶段忽略,直到上线后出现数据不一致才去查 oplog 截断日志。











