我知道MongoDB不像关系数据库那样支持事务,但我仍然想知道如何实现几个操作的原子性。在网络上狩猎,我看到有人提到Transactions without Transactions。通过幻灯片阅读,我仍然不清楚如何使用Mongoose.js实现它。
以此代码段为例:
player.save(callback1);
story.save(callback2);
如何实现callback1和callback2以使它们一起成功或一起失败?
答案 0 :(得分:9)
如果您确实必须跨多种文档类型(在单独的集合中)拥有事务,那么实现此目的的方法是使用单个表来存储要采取的操作。
db.actions.insert(
{ actions: [{collection: 'players', _id: 'p1', update: {$set : {name : 'bob'} } },
{collection: 'stories', _id: 's1', update: {$set : {location: 'library'} } }], completed: false }, callback);
此插入是原子的,一次完成。然后,您可以在' actions'中执行命令。收集并将它们标记为完成,或在完成时将其删除,在完成后调用原始回调。仅当您的操作处理循环是更新数据库的唯一内容时,此方法才有效。当然,你必须停止使用猫鼬,但是越早你做到这一点就越好。无论如何。
答案 1 :(得分:8)
这个问题相当陈旧,但对于偶然发现此页面的任何人,您都可以使用fawn。它是一个npm包解决了这个确切的问题。披露:我写了
假设您有两个银行账户,一个属于John Smith,另一个属于Broke Individual。您想将20美元从John Smith转移到Broke Individual。假设所有名字和姓氏对都是唯一的,这可能看起来像:
var Fawn = require("fawn");
var task = Fawn.Task()
//assuming "Accounts" is the Accounts collection
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
.update("Accounts", {firstName: "Broke", lastName: "Individual"}, {$inc: {balance: 20}})
.run()
.then(function(){
//update is complete
})
.catch(function(err){
// Everything has been rolled back.
//log the error which caused the failure
console.log(err);
});
警告: 任务当前没有被隔离(正在进行)因此,从技术上讲,两个任务可以检索和编辑同一个文档,因为这就是MongoDB的工作方式。
它实际上只是教程网站上两阶段提交示例的通用实现:https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/
答案 2 :(得分:6)
您可以通过在发生错误时手动回滚更改来模拟事务。在上面的示例中,如果另一个文档失败,只需删除已保存的文档。
您可以使用Async
function rollback (doc, cb) {
doc.remove(cb);
}
async.parallel([
player.save.bind(player),
story.save.bind(story),
],
function (err, results) {
if (err) {
async.each(results, rollback, function () {
console.log('Rollback done.');
});
} else {
console.log('Done.');
}
});
显然,回滚本身可能会失败 - 如果这是不可接受的,您可能需要重新构建数据或选择其他数据库。
注意:我在此post详细讨论了这个问题。
答案 3 :(得分:2)
开箱即用的MongoDB不支持事务。但是,它们可以通过Two-phase commit protocol实现。 Here您可以看到有关如何在MongoDB中执行2PC的官方建议。 这种方法非常普遍,可以应用于不同的场景,例如
答案 4 :(得分:2)
有一些尝试实施解决方案。在撰写本文时,他们都没有特别活跃。但也许他们工作得很好!
https://github.com/rain1017/memdb提供交易支持,但实际上做的不仅仅是6个月闲置。
niahmiah's README值得一看。它指出了使用交易的一些缺点,即:
niahmiah还表示可能有使用交易的替代方案。
只是提示:如果您不想锁定常用的集合,可以考虑将敏感(事务性)数据与不太敏感的数据分开。
例如,如果您当前在用户集合中持有credits
,那么最好将其分成两个集合:用户和< em> UserWallet ,并将credits
字段移动到钱包中。然后,您可以自由更新用户文档,而不必担心干扰 UserWallet 集合上的事务。
答案 5 :(得分:2)
Mongoose> = 5.2.0和MongoDB> = 4.0.0(带有副本集)现在支持事务