mongodb的事务与二阶段提交

简介

对于MongoDB数据库来说,单文档的操作是能够保证原子性的;但是设计多个文档的操作(即多文档事务)却不是原子性的。

其实单文档操作的原子性已经可以给大多数实际用例(译注:用例是指软件开发中用户的某一个需求)提供足够的支持,因为单个文档的结构可以很复杂甚至包括很多内嵌的文档。

虽然单文档原子操作的功能已经很强大了,但是仍然会有些情况需要多文档事务。当执行一个由一系列操作组成的事务时,某些问题就产生了,比如:

  • 原子性:如果一个操作失败了,事务中之前的操作必须“回滚”(rollback)(即”all or nothing”,要么全部发生要么全部不发生)
  • 一致性:如果某个严重的故障(比如网络故障,硬件故障等)打断了事务,数据库必须能够回复到一致的状态
    在需要多文档事务的情况下,你可以在你的应用中实现两阶段提交来给这些多文档更新提供支持。两阶段提交能够保证数据的一致性,并且在出现错误的情况下,可以将数据恢复到事务之前的状态。

注意

因为MongoDB中只支持单文档原子操作,所以两阶段提交只能提供类似于事务的语义(transaction-like semantics)。应用还是可以在两阶段提交或者回滚的中间点上返回中间数据。

例子

概述

现在从账户A转账给账户B 100。在传统的关系数据库中,你可以在一个事务中从账户A上减去金额并且在账户B上增加相应金额。在MongoDB中,你可以通过两阶段提交实现类似的效果。

这个例子使用如下两个集合(译注:在MongoDB中集合的概念类似于mysql中的一张表):

  1. 一个叫做accounts的集合用于存储账户信息。
  2. 一个叫做transactions的集合用于存储转账事务相关的信息。

具体过程

  1. https://www.jianshu.com/p/6b4f99cb5862
  2. 官方文档,获益很多

分析该过程的各个状态

0

{_id:"A", balance:1000, pendingTransaction:[]}
{_id:"B", balance:1000, pendingTransaction:[]}

1

{_id:"tx", A->B, state:"initial", lastModified: 1}
{_id:"A", balance:1000, pendingTransaction:[]}
{_id:"B", balance:1000, pendingTransaction:[]}

2

{_id:"tx", A->B, state:"pending", lastModified: 2}
{_id:"A", balance:1000, pendingTransaction:[]}
{_id:"B", balance:1000, pendingTransaction:[]}

3

{_id:"tx", A->B, state:"pending", lastModified: 2}
{_id:"A", balance:900, pendingTransaction:["tx"]}
{_id:"B", balance:1000, pendingTransaction:[]}

4

{_id:"tx", A->B, state:"pending", lastModified: 2}
{_id:"A", balance:900, pendingTransaction:["tx"]}
{_id:"B", balance:1100, pendingTransaction:["tx"]}

5

{_id:"tx", A->B, state:"applied", lastModified: 5}
{_id:"A", balance:900, pendingTransaction:["tx"]}
{_id:"B", balance:1100, pendingTransaction:["tx"]}

6

{_id:"tx", A->B, state:"applied", lastModified: 5}
{_id:"A", balance:900, pendingTransaction:[]}
{_id:"B", balance:1100, pendingTransaction:["tx"]}

7

{_id:"tx", A->B, state:"applied", lastModified: 5}
{_id:"A", balance:900, pendingTransaction:[]}
{_id:"B", balance:1100, pendingTransaction:[]}

8

{_id:"tx", A->B, state:"done", lastModified: 8}
{_id:"A", balance:900, pendingTransaction:[]}
{_id:"B", balance:1100, pendingTransaction:[]}

恢复与回滚(Recovering from Failure Scenarios)

The most important part of the transaction procedure is not the prototypical example above, but rather the possibility for recovering from the various failure scenarios when transactions do not complete successfully. This section presents an overview of possible failures and provides steps to recover from these kinds of events.

Recovery Operations

The two-phase commit pattern allows applications running the sequence to resume the transaction and arrive at a consistent state. (让所有的交易都被执行完,达到最终的状态)

Run the recovery operations at application startup, and possibly at regular intervals, to catch any unfinished transactions. (定时抓捕未完成的交易)

The time required to reach a consistent state depends on how long the application needs to recover each transaction.

The following recovery procedures uses the lastModified date as an indicator of whether the pending transaction requires recovery; specifically, if the pending or applied transaction has not been updated in the last 30 minutes, the procedures determine that these transactions require recovery. You can use different conditions to make this determination.

Transactions in Pending State

To recover from failures that occur after step “Update transaction state to pending.” but before “Update transaction state to applied.” step, retrieve from the transactions collection a pending transaction for recovery and resume from step “Apply the transaction to both accounts.”

Transactions in Applied State

To recover from failures that occur after step “Update transaction state to applied.” but before “Update transaction state to done.” step, retrieve from the transactions collection an applied transaction for recovery and resume from “Update both accounts’ list of pending transactions.”

Rollback Operations

In some cases, you may need to “roll back” or undo a transaction; e.g., if the application needs to “cancel” the transaction or if one of the accounts does not exist or stops existing during the transaction.

Transactions in Applied State

After the “Update transaction state to applied.” step, you should not roll back the transaction. Instead, complete that transaction and create a new transaction to reverse the transaction by switching the values in the source and the destination fields.

Transactions in Pending State

After the “Update transaction state to pending.” step, but before the “Update transaction state to applied.” step, you can rollback the transaction using the following procedure:

  1. Update transaction state to canceling.

  2. Undo the transaction on both accounts.

  3. Update transaction state to canceled.

另一个简单的例子

相关的github实现

猜你喜欢

转载自blog.csdn.net/jason_cuijiahui/article/details/80184616