MongoDB报E11000重复键错误UnhandledPromiseRejectionWarning如何解决
MongoDB E11000 重复键报错原因及修复方案
UnhandledPromiseRejectionWarning: MongoServerError: E11000 duplicate key error collection: Tourism.addedOffer index: _id dup key: { _id: "617b6e76110c116302d9afdd" }
核心含义
MongoDB的_id字段是集合默认的唯一主键索引,不允许重复值,当前要插入/更新的数据里,_id为617b6e76110c116302d9afdd的记录已经存在于Tourism.addedOffer集合中;同时代码未对Mongo操作返回的Promise异常做捕获处理,因此额外抛出未处理Promise拒绝的警告。
触发原因
- 显式指定
_id插入重复数据:新增文档时手动指定了已经存在的_id值,未做存在性校验直接执行插入操作 - upsert逻辑错误:执行更新操作时配置了
upsert: true,但查询条件命中了多条数据,或者_id字段被误写进更新内容触发重复 - 并发写入冲突:多请求/多进程同时写入同一条数据,未加锁或者做幂等判断,后发起的请求触发重复键错误
- 唯一索引配置异常:除了默认的
_id索引,额外给其他字段建了唯一索引,对应字段存在重复值也会触发同类型错误 - Promise异常未捕获:所有Mongo操作返回的都是Promise对象,未用
try/catch或者.catch()方法捕获异常,因此抛出UnhandledPromiseRejectionWarning警告
排查步骤
- 确认集合数据:执行Mongo命令
db.addedOffer.find({_id: ObjectId("617b6e76110c116302d9afdd")}),确认该_id对应的数据是否已经存在 - 检查代码逻辑:定位触发报错的Mongo操作代码,确认是插入、更新还是upsert操作,是否存在手动指定
_id的逻辑 - 检查索引配置:执行命令
db.addedOffer.getIndexes(),确认集合的唯一索引配置,排查是否有除了_id之外的其他唯一索引导致报错 - 检查异常捕获逻辑:确认对应Mongo操作是否做了异常捕获处理
修复方案
- 插入场景修复:
- 新增文档如果不需要自定义
_id,不要手动传_id字段,MongoDB会自动生成全局唯一的_id值 - 如果必须自定义
_id,插入前先执行查询判断该_id是否存在,不存在再执行插入操作,或者直接用insertOne加异常捕获,遇到重复键错误直接返回已存在提示
- 新增文档如果不需要自定义
- 更新场景修复:
- 执行更新操作时不要把
_id字段放到更新内容里修改 - upsert操作的查询条件要确保唯一,避免触发重复写入
- 执行更新操作时不要把
- 并发场景修复:
- 给写入操作加分布式锁,或者基于唯一键做幂等判断,直接过滤重复请求
- 异常捕获修复:
所有Mongo操作都加异常捕获,async/await写法用try/catch包裹,回调写法加.catch()处理异常,示例代码:// async/await写法 try { await db.collection('addedOffer').insertOne(doc) } catch (err) { if (err.code === 11000) { // 自定义重复键错误处理逻辑 console.log('数据已存在') } else { // 其他错误处理 console.error(err) } } // 回调写法 db.collection('addedOffer').insertOne(doc) .then(res => {}) .catch(err => { if (err.code === 11000) { // 自定义重复键错误处理逻辑 } }) - 重复数据清理:如果是测试环境误写入重复数据导致的索引创建失败,可以先删除重复数据后再重建索引。
内容的提问来源于stack exchange,提问作者Tosir
相关产品推荐
相关产品推荐

