Motor多文档事务需显式创建session并透传至所有操作,仅支持副本集或分片集群;必须用try/except/finally显式处理commit/abort,避免悬挂事务;事务内聚合和$lookup须建索引,且不适用于高并发长耗时场景。

Motor 多文档事务必须用 AsyncIOMotorClient.start_session() 显式开启
MongoDB 的多文档事务只在副本集或分片集群上可用,单节点(standalone)模式下调用 start_session() 不报错但实际不生效。Motor 本身不自动启用事务,必须手动创建 session 并传入所有操作中。
- ✅ 正确流程:先
async with await client.start_session(),再async with session.start_transaction() - ❌ 错误写法:
collection.insert_one(...)直接调用——这永远是无事务的普通写入 - ⚠️ 注意:
session对象不能跨协程复用,也不能被存为全局变量;每次事务必须新建 session 或明确复用(需保证线程/协程安全)
session 必须透传到所有参与事务的 collection 操作中
MongoDB 事务要求所有读写操作都绑定同一 session。Motor 的 find()、update_one()、delete_many() 等方法都支持 session= 参数,漏传即脱离事务上下文。
- ✅ 正确示例:
await users_col.update_one({"_id": uid}, {"$inc": {"balance": -100}}, session=session) - ❌ 常见坑:
await logs_col.insert_one({...})没传session→ 日志写入立即生效,事务回滚时它不会撤销 - ⚠️ 性能影响:事务内所有操作共享锁,长时间持有 session 会阻塞其他写请求;建议控制在 60 秒内完成
事务失败后必须显式 session.abort_transaction(),不能只靠 async with 自动清理
async with session.start_transaction() 只在正常退出时自动 commit;一旦发生异常,Uvicorn 默认不会触发 __aexit__ 的 rollback 分支(尤其在未捕获的未处理异常下),导致悬挂事务和连接泄漏。
- ✅ 推荐结构:用
try/except/finally显式调用abort_transaction()或commit_transaction() - ✅ 示例关键片段:
try: await session.start_transaction() await col_a.update_one(..., session=session) await col_b.insert_one(..., session=session) await session.commit_transaction() except Exception: await session.abort_transaction() raise - ⚠️ 容易忽略:即使
commit_transaction()抛出网络错误(如主节点切换),也需检查session.in_transaction状态并补 abort
事务内聚合管道、$lookup 和索引缺失会导致静默性能崩溃
事务本身不校验查询效率,但所有操作都在事务锁保护下串行执行。若事务中含未优化的聚合(如无索引 $lookup),整个事务会卡住,拖慢所有并发请求。
- ✅ 必做:对事务中所有
$lookup关联字段、sort字段、find查询条件字段,提前建好复合索引 - ❌ 危险操作:
collection.aggregate([{"$lookup": {...}}, {"$unwind": "$items"}])在事务中遍历万级文档 → 内存暴涨 + 超时中断 - ⚠️ 兼容性限制:MongoDB 6.0+ 才支持事务内部分聚合阶段(如
$facet不支持);低于此版本,聚合必须拆到事务外预计算



















