我有重复密钥的问题。很长一段时间找不到答案。请帮助我解决此问题或解释为什么我得到重复的密钥错误。
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)); };
导致插入文档的ups不是完全原子的操作。将ups视为执行以下离散步骤:
因此,第2步和第3步都是原子操作,但是在第1步之后可能会发生另一个upsert,因此您的代码需要检查重复的键错误,然后再次尝试upsert。到那时,您知道_id存在的文档,因此它将始终成功。
_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); } } });
有关相关文档,请参见此处。
您可能仍然想知道,如果插入是原子的,为什么会发生这种情况,但这意味着插入的文档在完全写入之前不会进行任何更新,而不是不会_id出现具有相同文档的其他插入。
另外,您不需要手动创建索引,_id因为所有MongoDB集合都具有唯一的索引_id。因此,您可以删除以下行:
monitorSchema.index({_id: -1}); // Not needed