MongoDB中的多集合,多文档“事务”

时间:2012-09-19 11:13:07

标签: mongodb transactions

我意识到MongoDB,就其本质而言,不会也可能永远不会支持这类交易。但是,我发现我确实需要以有限的方式使用它们,所以我想出了以下解决方案,我想知道:这是最好的方法,它可以吗改进了吗?(在我去我的应用程序之前实现它!)

显然,事务是通过应用程序控制的(在我的例子中,是一个Python Web应用程序)。对于此事务中的每个文档(在任何集合中),将添加以下字段:

'lock_status': bool (true = locked, false = unlocked),
'data_old': dict (of any old values - current values really - that are being changed),
'data_new': dict (of values replacing the old (current) values - should be an identical list to data_old),
'change_complete': bool (true = the update to this specific document has occurred and was successful),
'transaction_id': ObjectId of the parent transaction

此外,还有一个transaction集合,用于存储详细说明正在进行的每个事务的文档。他们看起来像:

{
    '_id': ObjectId,
    'date_added': datetime,
    'status': bool (true = all changes successful, false = in progress),
    'collections': array of collection names involved in the transaction
}

这是这个过程的逻辑。希望它以这样的方式工作:如果它被中断,或者以其他方式失败,它可以正常回滚。

1:设置transaction文档

2:对于受此次交易影响的每个文档:

  • lock_status设置为true(以“锁定”文档不被修改)
  • data_olddata_new设置为旧值和新值
  • change_complete设为false
  • transaction_id设置为我们刚刚制作的transaction文档的ObjectId

3:执行更新。对于每个受影响的文件:

  • 使用data_new
  • 替换该文档中的所有受影响的字段
  • change_complete设为true

4:transaction文档的status设置为true(因为所有数据都已成功修改)

5:对于受交易影响的每个文档,请进行一些清理:

  • 删除data_olddata_new,因为不再需要
  • lock_status设置为false(解锁文档)

6:删除步骤1中设置的transaction文档(或按照建议,将其标记为完成)


我认为逻辑上的工作方式是,如果它在任何时候都失败,所有数据都可以回滚或者事务可以继续(取决于你想做什么)。显然所有的回滚/恢复/等。由应用程序而不是数据库执行,使用transaction文档和其他集合中具有该transaction_id的文档。

我错过或忽略了这个逻辑中是否有任何明显的错误?是否有更有效的方法(例如,从数据库中减少写入/读取)?

3 个答案:

答案 0 :(得分:11)

作为通用响应,MongoDB上的多文档提交可以作为两个阶段提交来执行,这些提交在手册中有详细记录(参见:http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/)。

本手册建议的模式简要介绍如下:

  • 设置单独的transactions集合,其中包括目标文档源文档状态< / em>(交易)
  • 使用initial作为state
  • 创建新的交易对象
  • 开始制作交易并将state更新为pending
  • 将交易应用于两个文件(目标,来源)
  • 将交易状态更新为committed
  • 使用find确定文档是否反映了事务状态,如果可以,将事务状态更新为done

另外:

  • 您需要手动处理故障情况(某些事情未发生,如下所述)
  • 您需要手动实现回滚,主要是通过引入名称statecanceling

您的实施的一些具体说明:

  • 我不鼓励您将lock_statusdata_olddata_new等字段添加到源/目标文档中。这些应该是事务的属性,而不是文档本身。
  • 为了概括目标/源文档的概念,我认为您可以使用DBref s:http://www.mongodb.org/display/DOCS/Database+References
  • 我不喜欢在完成后删除交易文件的想法。将状态设置为done似乎是一个更好的主意,因为这允许您稍后调试并找出已执行的事务类型。我很确定你也不会耗尽磁盘空间(为此也有解决方案)。
  • 在您的模型中,您如何保证所有内容都按预期更改?你是否以某种方式检查了这些变化?

答案 1 :(得分:3)

MongoDB 4.0添加了对多文档ACID事务的支持。

Java示例:

try (ClientSession clientSession = client.startSession()) {
   clientSession.startTransaction();
   collection.insertOne(clientSession, docOne);
   collection.insertOne(clientSession, docTwo);
   clientSession.commitTransaction();
}

答案 2 :(得分:0)

MongoDB 4.0正在添加(多集合)多文档事务:link