带有 upsert 的 Mongoose 重复键错误
Posted
技术标签:
【中文标题】带有 upsert 的 Mongoose 重复键错误【英文标题】:Mongoose duplicate key error with upsert 【发布时间】:2016-09-14 16:56:53 【问题描述】:我有重复键的问题。 很久没找到答案。请帮我解决这个问题或解释为什么我得到重复键错误。
Trace: [MongoError: E11000 duplicate key error collection: project.monitor index: _id_ dup key: : 24392490 ]
name: 'MongoError',
message: 'E11000 duplicate key error collection: project.monitor index: _id_ dup key: : 24392490 ',
driver: true,
index: 0,
code: 11000,
errmsg: 'E11000 duplicate key error collection: project.monitor index: _id_ dup key: : 24392490 '
at /home/project/app/lib/monitor.js:67:12
at callback (/home/project/app/node_modules/mongoose/lib/query.js:2029:9)
at Immediate.<anonymous> (/home/project/app/node_modules/kareem/index.js:160:11)
at Immediate._onImmediate (/home/project/app/node_modules/mquery/lib/utils.js:137:16)
at processImmediate [as _immediateCallback] (timers.js:368:17)
但是在监视器中我使用了 upsert,那为什么会出现重复错误??
monitor.js:62-70
监控架构
var monitorSchema = db.Schema(
_id : type: Number, default: utils.minute,
maxTicks : type: Number, default: 0,
ticks : type: Number, default: 0,
memory : type: Number, default: 0,
cpu : type: Number, default: 0,
reboot : type: Number, default: 0,
streams : db.Schema.Types.Mixed
,
collection: 'monitor',
strict: false
);
索引
monitorSchema.index(_id: -1);
Monitor = db.model('Monitor', monitorSchema);
按属性增加
exports.increase = function (property, incr)
var update = ;
update[property] = utils.parseRound(incr) || 1;
Monitor.update(_id: utils.minute(), $inc: update, upsert: true, function (err)
if (err)
console.trace(err);
);
;
utils.js
exports.minute = function ()
return Math.round(Date.now() / 60000);
;
exports.parseRound = function (num, round)
if (isNaN(num)) return 0;
return Number(parseFloat(Number(num)).toFixed(round));
;
【问题讨论】:
不要试图自己管理“_id”,除非非常非常必要... 在我的任务中是非常非常必要的。 没人知道我为什么会收到这个错误? 下面提到的答案是正确的检查...考虑一下.. 【参考方案1】:导致文档插入的 upsert 不是完全原子操作。将 upsert 视为执行以下离散步骤:
-
查询要更新的已识别文档。
如果文档存在,自动更新现有文档。
否则(文档不存在),自动插入包含查询字段和更新的新文档。
所以第 2 步和第 3 步都是原子的,但在第 1 步之后可能会发生另一个 upsert,因此您的代码需要检查重复键错误,然后在发生这种情况时重试 upsert。此时,您知道带有 _id
的文档存在,因此它将始终成功。
例如:
var minute = utils.minute();
Monitor.update( _id: minute , $inc: update , upsert: true , function(err)
if (err)
if (err.code === 11000)
// Another upsert occurred during the upsert, try again. You could omit the
// upsert option here if you don't ever delete docs while this is running.
Monitor.update( _id: minute , $inc: update , upsert: true ,
function(err)
if (err)
console.trace(err);
);
else
console.trace(err);
);
有关相关文档,请参阅here。
您可能仍然想知道如果插入是原子的,为什么会发生这种情况,但这意味着插入的文档在完全写入之前不会发生更新,而不是没有其他具有相同 @ 的文档插入987654324@ 可能会出现。
此外,您无需在 _id
上手动创建索引,因为无论如何,所有 MongoDB 集合在 _id
上都有唯一索引。所以你可以删除这一行:
monitorSchema.index(_id: -1); // Not needed
【讨论】:
你想说,我需要更改 .update 方法来查找+更新\插入? @cheks 不,我是说这些是update
upsert 自己执行的步骤。这就是为什么您无法避免遇到重复键错误的可能性,因此您必须在代码中检查错误并从中恢复。
我以前也这么认为,但我不确定它的最佳方式。谢谢!以上是关于带有 upsert 的 Mongoose 重复键错误的主要内容,如果未能解决你的问题,请参考以下文章
Mongodb/Mongoose bulkwrite(upsert) 性能问题
(mongoose/promises) 如何检查文档是不是是使用 findOneAndUpdate 和 upsert 创建的