我遇到重复密钥的问题。 好久不能找到答案。请帮我解决这个问题或解释为什么我会出现重复的密钥错误。
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));
};
答案 0 :(得分:19)
导致文档插入的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。
您可能仍然想知道如果插入是原子的,为什么会发生这种情况,但是这意味着在完全编写插入文档之前不会发生更新,而不是没有其他插入的文档具有相同的{ {1}}可能会发生。
此外,您无需在_id
上手动创建索引,因为所有MongoDB集合都在_id
上具有唯一索引,无论如何。所以你可以删除这一行:
_id