【问题标题】:Multi-collection, multi-document 'transactions' in MongoDBMongoDB 中的多集合、多文档“事务”
【发布时间】:2012-09-11 16:40:45
【问题描述】:

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

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

'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 的其他集合中的文档。

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

【问题讨论】:

    标签: mongodb transactions


    【解决方案1】:

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

    Java 示例:

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

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

    【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

      另外:

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

      一些具体的实施说明:

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

      【讨论】:

      • DBrefs 的建议很好。但是这种两阶段提交方法是否适用于多个文档(不会变得混乱?)-在我的场景中,我需要同时更新一个集合中的 1 个文档和另一个集合中的 n 个文档(数量不同)。跨度>
      • 我认为您也可以添加 DBref 数组。
      • 我会听取您的建议,不会删除交易文件。使用change_complete 字段可以保证在文档到文档的基础上按预期进行更改。
      • 如果一次更新失败而您仍然将change_complete 更新为true 怎么办?您如何确保不会发生这种情况?
      • 好的。此外,如果由于任何原因应用程序无法在事务设置为完成后将 lock_status 更新为 false 怎么办? IE。你如何确保只有当前交易的文件被锁定?即使交易成功,它们也会给您留下锁定的文档,从而阻止进一步的交易和更新。
      猜你喜欢
      • 2020-09-16
      • 1970-01-01
      • 1970-01-01
      • 2019-05-28
      • 1970-01-01
      • 1970-01-01
      • 2017-10-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多