hdwgxz

Mongodb不支持多文档原子性操作,因此依据两阶段提交协议(Two Phase Commits protocol)来模拟事务。

以两个银行账户之间的转账行为为例,来说明如何实现多文档间的事务操作。

为实现多文档间的事务操作,定义一个事务文档TransactionDocument,储存在事务集合TransactionCollection

 

public class TransactionDocument2
    {
        public object _id { set; get; }
        //原账户
        public string Source { set; get; }
        //目标账户
        public string Destination { set; get; }
        //转账金额
        public decimal Value { set; get; }
        //执行状态(初始化initial, 执行操作pending, 完成操作applied, 事务结束done, 正在取消操作canceling, 完成取消canceled)
        public string State { set; get; }
        //最后修改日期
        public DateTime LastModified { set; get; }
    }

 

银行账户的结构为:

public class Account
    {
        /// <summary>
        /// 账号
        /// </summary>
        public string _id { set; get; }
        /// <summary>
        /// 账户余额
        /// </summary>
        public decimal Balance { set; get; }
        /// <summary>
        /// 待处理事务链表
        /// </summary>
        public List<object> PendingTransactions { set; get; }
}

 

转账的主流程为:

0步,为参与事务的两个实体创建唯一的事务文档。

AB两个账户创建唯一的事务文档,事务文档的_id值为AB账户_id值的组合。

1步,在TransactionCollection集合中找到状态为"initial"的事务文档。

对于AB两个账户间的转账操作,只能有一个事务文档。这样做是为了防止多个客户端同时对一个账户执行修改操作,只有一个这种事务文档,那么当AB间的转账行为开始时,事务文档的状态为“pending”,而事务开始要查找的是状态为“initial”的事务文档,因此不会获得这样的事务文档,也就不会执行任何操作,只有当AB转账操作完成后才有可能再次执行类似的操作。

2步,第1步执行成功的前提下,将事务文档状态由“initial”更改为“pending”。

3步,第2步执行成功的前提下,对两个账户应用事务,执行转账。

对两个账户应用事务的具体操作就是向AB两个账户的待处理事务链表中添加事务文档_id

4步,第3步执行成功的前提下,将事务文档状态由“pending”更改为“applied”。

5步,第4步执行成功的前提下,移除事务标识。

具体操作是:移除第3步中向AB两个账户待处理事务链表添加的事务文档_id

6步,第5步执行成功的前提下,将事务文档状态由“applied”更改为“done”。

7步,不论第6步是否执行成功,将事务文档状态“done”更改为“initial”。

看似在第6步将applied”更改为“initial也是可以的,但是如果在这之间在加入一个done”状态会带来更大的好处,例如,可以定时扫描TransactionCollection集合,批量将状态为“done”的事务文档状态改为“initial”,而不是在第6步执行完成以后立即执行第7步。

 

辅助流程

针对上述主流程的每一步加以分析,找出需要辅助流程介入的位置。

对于第0步:

如果创建不成功不会产生任何影响。

对于第1步:

如果没有找到,不会产生任何影响。

对于第2步:

如果事务文档状态修改不成功,不会产生任何影响。

对于第3步:

如果执行转账失败,A账户的钱已被扣除V,但B没有收到V,回滚到之前的状态。

如果在指定的超时时间内没有完成则,执行从错误中恢复策略。

对于第4步:

如果修改事务文档状态失败,设置执行超时时间Th4,重复执行此步骤,如果超时时间已到达,但未完成,执行从错误中恢复策略。

对于第5步:

如果移除事务标识失败,设置执行超时时间Th5,重复执行此步骤,如果超时时间已到达,但未完成,执行从错误中恢复策略。

对于第6步:

如果移除事务标识失败,设置执行超时时间Th6,重复执行此步骤,如果超时时间已到达,但未完成,执行从错误中恢复策略。

 

回滚的步骤为:

1步,将事务文档状态由pending”更改为“canceling

2步,账户余额还原为操作之前的状态,删除两个账户的待处理事务链表中的事务文档_id.

3步,将事务文档状态由canceling”更改为“cancelled

 

从错误中恢复策略

通过重复执行需要此策略的那一步操作即可达到目的。可以选择异步执行错误恢复机制。

 

超时检测

比较事务文档的LastModified 与当前时间的值,如果二者差值超过设定的阈值,即判定超时。

 

相关文章: