多文档事务是什么
多文档事务让一个会话里的多条读写要么全部生效要么全部回滚。复制集从 4.0 起支持,分片集群从 4.2 起支持。用法是取出会话(startSession),在会话上开启事务,用会话作用域的集合句柄执行读写,最后提交或回滚。事务有生命周期上限 transactionLifetimeLimitSeconds(默认 60 秒),超时会被服务端中止;事务内不适合执行建集合、建索引这类 DDL,也不应该等待外部接口。
基本信息
| 版本要求 | 复制集 4.0+,分片集群 4.2+ |
|---|---|
| 创建会话 | db.getMongo().startSession() |
| 开始与结束 | startTransaction / commitTransaction / abortTransaction |
| 生命周期 | 默认 60 秒(transactionLifetimeLimitSeconds) |
| 读关注 | 事务内可用 snapshot 或 majority |
| 资源释放 | 结束时要调用 endSession() |
典型使用场景
- 转账与对账:扣款和入账必须同时成功或同时失败
- 订单扣库存:一次下单涉及多个集合时保证原子变更
- 审计落地:业务写入与审计流水一起提交,避免只留下一半
- 幂等去重:去重表记录与业务数据在同一事务里写入
- 跨分片一致性:分片键不同的数据需要一起变更时
mongosh 示例
mongosh 示例
const session = db.getMongo().startSession() // 取出会话
const coll = session.getDatabase("app").orders // 会话作用域的集合句柄
session.startTransaction() // 开启事务
coll.insertOne({ _id: 3001, sku: "T-1", qty: 1 }) // 事务内写入
coll.updateOne({ _id: 3001 }, { $set: { status: "paid" } })
session.commitTransaction() // 提交
try { /* 业务逻辑 */ } catch (e) { session.abortTransaction() } // 异常回滚
session.endSession() // 释放会话资源会话操作的顺序
| 操作 | 作用 | 注意 |
|---|---|---|
| startSession | 创建会话 | 用完要 endSession,避免资源堆积 |
| startTransaction | 开启事务 | 同一会话内不能再嵌套开新事务 |
| 读写 | 用会话句柄操作集合 | 用外层句柄写的数据不在事务里 |
| commitTransaction | 提交 | 写冲突时可能抛出可重试错误 |
| abortTransaction | 回滚 | 异常分支必须保证被执行 |
事务持锁并占用缓存与 oplog,要尽量短小。不要在事务里等待外部接口或人工确认,否则很容易撞上 60 秒上限并被服务端中止。
事务里的写操作必须使用会话作用域的集合句柄;用外层 db 句柄执行的那部分写入不在事务范围内,回滚时不会被撤销,这是最容易踩的坑。