带有 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 重复键错误的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose upsert 重复键错误

Mongodb/Mongoose bulkwrite(upsert) 性能问题

(mongoose/promises) 如何检查文档是不是是使用 findOneAndUpdate 和 upsert 创建的

如何使用基于 _id 的 Mongoose 进行 upsert?

Mongoose upsert ?????????

runValidator 属性无法在 mongodb 上使用 mongoose 进行 upsert