如何解决MongoDB会话中‘Retryable write with txnNumber被禁止’报错?
问题排查与解决
错误原因
这个报错的核心是同一会话中,低事务号(txnNumber)的重试写操作晚于高事务号的操作完成。MongoDB会为会话内的每个重试写操作分配递增的txnNumber,当并行发起会话操作时,网络延迟或数据库内部调度可能导致高txnNumber的操作先被处理,后续低txnNumber的重试请求会被判定为过期,触发该错误。
同时你的代码存在两个基础拼写错误:
aysnc应为asyncfinal应为finally
解决方案
方案1:开启显式事务(推荐,真正保证数据一致性)
既然需要保证数据一致性,应该使用MongoDB的事务而非单纯的会话。事务会严格控制操作顺序,避免txnNumber乱序问题:
async function createDevice () { const session = await mongoose.startSession(); try { // 开启事务 session.startTransaction(); const aCreated = await collectionA.create({}, { session }); const bCreated = await collectionB.create({}, { session }); await collectionA.updateOne( { _id: aCreated._id }, { $set: { bId: bCreated._id }}, { session } ); // 事务内操作需串行执行,避免并行导致的顺序问题 await collectionC.create({}, { session }); await collectionD.update({}, {}, { session }); // 提交事务 await session.commitTransaction(); } catch (err) { // 出错时回滚事务 await session.abortTransaction(); throw err; // 抛出错误便于排查定位 } finally { await session.endSession(); } }
方案2:确保会话操作串行+关闭重试写(不推荐,仅临时解决)
如果暂时不需要事务,需保证会话内的操作严格串行,并关闭重试写机制(会丢失重试能力):
async function createDevice () { const session = await mongoose.startSession(); try { const aCreated = await collectionA.create({}, { session, retryWrites: false }); const bCreated = await collectionB.create({}, { session, retryWrites: false }); await collectionA.updateOne( { _id: aCreated._id }, { $set: { bId: bCreated._id }}, { session, retryWrites: false } ); // 串行执行所有会话操作 await collectionC.create({}, { session, retryWrites: false }); await collectionD.update({}, {}, { session, retryWrites: false }); } catch (err) { // 处理错误逻辑 } finally { await session.endSession(); } }
关键注意点
- 同一会话(session)不支持并行操作,MongoDB会话是绑定到单一逻辑上下文的,操作必须顺序执行。
- 若要保证数据一致性,显式开启事务是唯一可靠的方式,单纯使用会话无法保证原子性。
内容的提问来源于stack exchange,提问作者Chicky
相关产品推荐
相关产品推荐

