20

我意识到,就其本质而言,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_statustrue(“锁定”文档不被修改)
  • 设置它们的旧值data_olddata_new新值
  • 设置change_completefalse
  • 设置为我们刚刚制作transaction_id的文档的ObjectIdtransaction

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

  • 将该文档中的任何受影响的字段替换为data_new
  • 设置change_completetrue

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

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

  • 删除data_oldand data_new,因为它们不再需要
  • 设置lock_statusfalse(解锁文档)

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


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

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

4

3 回答 3

13

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

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

  • 设置一个单独的transactions集合,包括目标文档源文档价值状态(交易的)
  • 创建新的交易对象initial作为state
  • 开始进行交易并更新statepending
  • 将事务应用于两个文档(目标、源)
  • 将交易状态更新为committed
  • 使用find判断文档是否反映事务状态,如果ok,更新事务状态为done

此外:

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

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

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

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

Java 示例:

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

注意,它适用于副本集。您仍然可以拥有一个具有一个节点的副本集并在本地计算机上运行它。

于 2018-07-02T10:30:08.993 回答
0

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

于 2018-06-25T16:09:09.793 回答