游乐游手机版
首页/数据库/文章详情

如何优雅捕获Node.js项目中的MongoDB写入异常

时间:2026-07-08 07:03
批量插入失败时不会抛出统一错误类型,需关注错误代码和写入错误数组。部分失败时可设置有序参数为假以获取写入错误数组。事务中写入失败会自动中止事务,提交事务不会执行。常见错误码11000为唯一键冲突,121为文档验证失败。

先说一个核心判断:insertMany 写入失败时,并不会抛出一个统一的错误类型。你可能会遇到 MongoWriteConcernErrorMongoBulkWriteErrorMongoServerError(比如 code 11000 代表重复键),甚至原生 Error(网络中断)。关键不在于看 error.name,而是要盯住 error.codeerror.writeErrors——对于批量写入来说,后者尤其重要。

Node.js项目中如何优雅地捕获MongoDB写入异常?

写入失败时 insertMany 抛什么错误?

实际上,MongoDB 驱动会根据具体失败原因返回不同错误对象:写关注没满足,给你 MongoWriteConcernError;批量写入中部分失败,给你 MongoBulkWriteError;常规服务端错误如重复键,则是带 code: 11000MongoServerError;当然还有网络中断这种原生 Error。所以判断逻辑要围绕 error.codeerror.writeErrors 展开,别只盯着 error.name——它往往不靠谱。

为什么 try/catch 捕不到“控制台没输出”?

这种情况十有八九是变量名引用问题。举个例子:你明明用 const Model = require("./models/match") 导入,结果在路由里写了 await Match.insertMany(...)。那好,Match is not defined 这个 ReferenceError 在函数刚开始执行时就被抛出了——同步语法错误,try/catch 根本抓不住,因为整个 async 函数体还没走到 await 那一行,当然不会有任何输出。

所以检查顺序很简单:

  • 核对模型中使用的变量名和 require 时定义的名字是否完全一致,大小写、拼写都不能马虎。
  • 确认模型已正确导出,没有丢三落四。
  • catch 块里加上 console.error(error.stack) 而不是只打印 error.message——堆栈信息能告诉你错在哪一行。

insertMany 部分失败时怎么拿到具体哪几条错了?

insertMany 默认走 ordered: true,即遇到第一条错误就立刻刹车。但只要改成 ordered: false,它就会继续处理剩余数据,最后通过 error.writeErrors 数组把每条失败项的索引、错误码和详情全抛给你:

try {  const result = await Model.insertMany(data, { ordered: false });} catch (error) {  if (error.writeErrors) {    error.writeErrors.forEach(({ index, code, errmsg }) => {      console.warn(`第 ${index} 条数据写入失败(code ${code}):`, errmsg);    });  }}

这里有几个常见错误码值得记住:

  • code: 11000 —— 唯一键冲突,解决方法通常是去重,或者改用 updateOne 配合 upsert
  • code: 121 —— 文档验证失败,回头看看 schema 里的 requiredvalidate 规则是不是太严了。
  • 另外,ordered: false 虽然不影响成功写入的数据,但错误堆栈会明显变长,生产环境最好加上日志采样,免得被海量错误信息刷屏。

事务中写入失败,commitTransaction 还会执行吗?

答案是否定的。commitTransaction 只有在显式调用时才会触发,而一旦某次 collection.insertOne 失败并抛出异常,驱动会自动 abort 当前事务,commitTransaction 那行代码根本执行不到。所以你能拿到错误码的地方只有两个:要么是 CRUD 操作本身(比如 insertOnecode: 133 写冲突),要么是显式调用 commitTransaction 时(可能报 code: 112251)。

  • 千万不要在 catch 里默认事务已经提交——老老实实检查 session.inTransaction() 的状态。
  • 注意 code: 112 并不代表写冲突(写冲突是 133),它更可能意味着会话过期或事务超时。这时候要结合 error.errorLabels?.includes('TransientTransactionError') 来判断能否重试。
  • 如果事务内某个操作因为 maxTimeMS 设置太短而超时,错误码是 code: 50MaxTimeMSExpired),这发生在具体命令层,可不是 commit 阶段的问题。

说到这里,核心结论就很清晰了:事务写入异常的根因往往不在 commit 那一刻,而在你第一次调用 insertOneupdateMany 的瞬间。错误码早就藏在那里了,别等到 commitTransaction 才去找原因。

来源:https://www.php.cn/faq/2784736.html
上一篇如何用SQL窗口函数ROW_NUMBER实现分组内去重 下一篇Redis订阅者BGSAVE期间响应变慢:RDB持久化IO影响分析
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多