Mongoose多集合事务实现方法及常见报错解决方案
Mongoose事务问题解答
问题1:操作未随事务回滚的解决方案
你所有需要纳入原子事务的数据库操作,都必须显式绑定当前创建的事务会话,未传入session参数的操作会走默认独立会话,不会被事务管控,自然不会回滚。修改后的业务代码如下:
const registerCustomer = async (req, res) => { const session = await mongoose.startSession(); session.startTransaction(); try { // 给更新操作传入session参数 await CustomerRegistrationCode.findByIdAndUpdate(req.body._id, { used: true }, { session: session }); // 给创建操作传入session参数 const customer = await Customer.create([{ firstName: req.body.firstName }], { session: session }); await session.commitTransaction(); session.endSession(); res.status(200).json(customer); } catch (error) { console.error('abort transaction'); await session.abortTransaction(); session.endSession(); res.status(500).json(error); throw error; } }
注意create方法传入session时,第一个参数需要是数组格式,不同Mongoose版本兼容该写法。
问题2:官方示例报错的解决方案
报错Transaction numbers are only allowed on a replica set member or mongos的原因是MongoDB事务仅支持副本集或者分片集群运行环境,本地默认启动的单节点独立模式不支持事务功能。你可以按照以下步骤快速启动本地单节点副本集:
- 关闭本地正在运行的MongoDB进程
- 执行命令以副本集模式启动Mongo:
mongod --port 27017 --dbpath /你的数据库存储路径 --replSet rs0 - 新开终端进入Mongo shell,执行副本集初始化命令:
rs.initiate()
等待10-30秒初始化完成后,即可正常运行事务相关代码。
可运行最小完整示例
const mongoose = require('mongoose'); const assert = require('assert'); const debugMongo = async () => { // 连接数据库,确保已经启动本地单节点副本集 await mongoose.connect("mongodb://localhost:27017/mongotest"); // 定义测试模型 const Customer = mongoose.model('Customer', new mongoose.Schema({ name: String })); const CustomerRegistrationCode = mongoose.model('CustomerRegistrationCode', new mongoose.Schema({ code: String, used: Boolean })); // 提前插入一条测试用的注册码 await CustomerRegistrationCode.create({ code: 'test123', used: false }); const testCode = await CustomerRegistrationCode.findOne({ code: 'test123' }); // 开启事务 const session = await mongoose.startSession(); session.startTransaction(); try { // 绑定session执行操作 await CustomerRegistrationCode.findByIdAndUpdate(testCode._id, { used: true }, { session }); // 这里故意写错字段触发错误,测试回滚 await Customer.create([{ wrongField: 'Test' }], { session }); await session.commitTransaction(); } catch (error) { console.log('事务回滚触发'); await session.abortTransaction(); } finally { session.endSession(); } // 验证回滚结果:used字段仍然是false const checkCode = await CustomerRegistrationCode.findById(testCode._id); assert.equal(checkCode.used, false); console.log('事务回滚验证通过'); await mongoose.disconnect(); } debugMongo();
内容的提问来源于stack exchange,提问作者swftowu69
相关产品推荐
相关产品推荐

