mongodb多文档事务需显式会话管理、readconcern/writeconcern设为"majority"、限于支持命令且不可跨分片键修改,否则退化为普通操作;事务内操作须幂等,超时自动abort,重试从头执行。

MongoDB 多文档事务能提供 ACID 一致性,但不是开箱即用的——它依赖于显式会话管理、正确配置的读写关注(readConcern 和 writeConcern),以及严格限制的操作范围。脱离这些条件,事务会退化为普通操作,失去原子性和隔离性。
必须使用 ClientSession 启动事务
事务只能在逻辑会话中运行,且该会话必须由同一个 MongoClient 实例创建。跨客户端复用 ClientSession 会直接报错,比如 InvalidSession 或 SessionNotFound。
常见错误现象:
- 在不同 HTTP 请求间传递 session ID 并试图复用
ClientSession对象 → 报InvalidSession - 每次事务都新建
MongoClient→ 事务无法启动,或提示Transaction numbers are only allowed on a replica set member or mongos
实操建议:
- 一个请求生命周期内只调用一次
client.start_session() - 将
ClientSession作为参数传入业务函数,不要尝试序列化/反序列化它 - 确保
MongoClient是单例或连接池共享实例,而非按需新建
readConcern: "majority" 和 writeConcern: "majority" 不可省略
这是因果一致性和 ACID 隔离性的硬性前提。缺一不可。默认值虽是 "majority",但显式指定更安全——尤其当全局配置被修改过时。
为什么这样做:
-
readConcern: "majority"确保你读到的是已提交到大多数节点的数据,避免读到回滚前的临时状态 -
writeConcern: "majority"确保写入被多数节点确认,否则事务提交可能失败并触发自动重试或抛出TransientTransactionError - 若使用
"local"或"available",事务内读操作可能看到未提交数据,或读不到刚写入的文档
示例(PyMongo):
一款AI工具,主要用于管理 OpenClaw 所使用的来自 OpenRouter 的免费 AI 模型。自动按质量对模型进行排序,配置回退机制以应对速率限制,并更新 opencla...,适合需要提升相关任务效率的用户。
with client.start_session() as session:
session.start_transaction(
read_concern=ReadConcern("majority"),
write_concern=WriteConcern("majority")
)
# ... 执行 insert_one / update_one 等
事务内操作必须满足支持列表,且不能跨分片键乱改
MongoDB 事务不支持所有命令,比如 $out、$merge、$lookup(跨库时)、$where、$near 等都会直接报错 CommandNotSupported 或 InvalidOptions。
容易踩的坑:
- 在事务里调用
countDocuments()→ 内部含$where表达式时失败;应改用aggregate([{"$count": "total"}]) - 对分片集合执行
distinct()→ 不支持,必须用aggregate + $group - 试图在事务中更新分片键字段(如非
_id的分片键)→ 失败,除非用findOneAndUpdate且目标字段允许变更
性能影响提示:事务内聚合阶段越多、文档越宽,快照内存占用越高,超时风险越大。建议把复杂计算移出事务,只留核心数据变更。
事务超时与自动重试机制的实际行为
MongoDB 默认事务最大运行时间是 60 秒(maxTransactionTimeMS),超时后自动 abort。而驱动层(如 PyMongo)对 TransientTransactionError 默认重试最多 3 次——但这不等于“自动修复”,只是重新执行整个回调函数。
关键点:
- 重试时不会恢复中间状态,而是从头开始跑事务块,所以事务内操作必须是幂等的(例如避免
insertOne无唯一约束) - 手动调用
session.commit_transaction()前若发生网络中断,可能留下prepared状态事务,需靠后台清理或手动干预 - 跨分片事务失败概率更高,因涉及协调器通信,建议监控
transactionCoordinatorStats指标
真正容易被忽略的地方:事务不是万能锁。它不阻止其他会话对同一文档加写锁(WiredTiger 层仍会阻塞),但也不提供传统 RDBMS 的行级锁语义——冲突检测靠时间戳+MVCC,发现冲突才回滚,不是提前抢占。










