Mongoose 事务怎么用?乐观锁和悲观锁有什么区别?
Mongoose 事务依赖 MongoDB 副本集(单机不支持),通过 session 实现多文档原子操作。核心流程:startSession() 创建会话,session.startTransaction() 开启事务,操作时每条 CRUD 必须传 { session } 参数,最后 commitTransaction() 或 abortTransaction(),endSession() 释放资源。推荐用 withTransaction() 辅助方法自动提交/回滚。常见坑:忘记在操作中传 session 参数导致操作不在事务内、事务超时默认 60 秒需控制时长。并发控制方面,Mongoose 内置乐观锁:Schema 的 __v 字段在 save() 时自动递增,若文档已被修改则抛 VersionError;悲观锁需自行实现,通常用字段锁标记 + findOneAndUpdate 原子设置。
追问
withTransaction() 和手动管理事务有什么区别? withTransaction(fn) 自动处理 commit/abort,fn 返回时自动提交,fn 抛异常自动回滚,遇到 TransientTransactionError 还会自动重试。手动管理则需自己 try/catch 并调 commit/abort。
哪些操作不支持事务? 创建集合、创建索引等 DDL 操作在 4.4 之前不能在事务中执行。事务内也不能操作 config、admin 等系统数据库的集合。
乐观锁的 __v 在什么场景下会失效? 使用 updateOne/findOneAndUpdate 等不经过 save() 的操作时 __v 不会自动检查。需要手动在条件中加入版本号:{ _id, __v: currentVersion }。
事务中读写同一个文档会死锁吗? 可能。两个事务同时修改同一文档会产生写冲突,后提交的事务会抛 WriteConflict 错误。解决:控制事务粒度、按固定顺序访问文档、加重试逻辑。
写段代码
javascriptconst session = await mongoose.startSession(); await session.withTransaction(async () => { await Account.updateOne( { _id: fromId, balance: { $gte: amount } }, { $inc: { balance: -amount } }, { session } ); await Account.updateOne( { _id: toId }, { $inc: { balance: amount } }, { session } ); }); session.endSession();