简单聊聊事务补偿机制

假设有如下的业务流程,用户1给用户2转账100元:

 

 转账服务需要执行如下操作:

第1步. 在数据库连接1上执行:update 用户表 set (用户1的余额) = (用户1的余额)- 100;

第2步. 在数据库连接2上执行:update 用户表 set (用户2的余额) = (用户2的余额)+ 100;

可能的问题:

1:第1步操作过程中,数据库1挂了,转账服务无法得知对用户1的扣款操作是否成功;

2:第1步操作成功,第2步操作失败,转账服务回滚第1步的操作时,数据库1挂了;

3:第1步操作成功,第2步操作过程中,数据库2挂了,转账服务无法得知是否成功给用户2加了钱;

 

基于上面的问题,产生了如下的数据库设计:

转账流程变成了如下步骤:

第1步:

    转账服务生成一个事务号,全局唯一;

第2步:转账服务在数据库1上执行事务:

    开始事务:

        update 用户表 set (用户1的余额) = (用户1的余额)- 100;

        insert 事务表 (事务号,成功)

    结束事务:

第3步:转账服务在数据库2上执行事务:

    开始事务:

        update 用户表 set (用户2的余额) = (用户2的余额)+ 100;

        insert 事务表 (事务号,成功)

    结束事务:

 

这样做的好处

    当操作用户1的账户失败时,转账服务可以通过再次查询数据库1的事务表来判断操作是否成功;

    当操作用户2的账户失败时,转账服务可以通过再次查询数据库2的事务表来判断操作是否成功;

接下来的问题:

    当转账服务更新用户1的账户成功后,接下来转账服务更新用户2的账户之前,转账服务自己挂了;

    这时,用户1被扣了100,但是用户2没多出来100,数据不一致;

 

新的数据库设计产生了,如下:

    

 

接下来的操作步骤变成了这样:

转账服务的操作:

    第1步:生成全局唯一事务号;生成事务号对应的时间戳;

    第2步:在回滚库的日志表中插入---“事务号开始”的操作;

    第3步:在回滚库的日志表插入---“扣除用户1的账户100元“的操作;

    第4步:在数据库1上执行事务:

        开始事务:

            update 用户表 set (用户1的余额) = (用户1的余额)- 100;

            insert 事务表 (事务号,成功)

        结束事务:

    第5步:在回滚库的日志表插入---“增加用户2的账户100元”的操作;

    第6步:在数据库2上执行事务:

        开始事务:

            update 用户表 set (用户2的余额) = (用户2的余额)+ 100;

            insert 事务表 (事务号,成功)

        结束事务:


    第7步:在回滚库的日志表插入---“事务号结束”的操作;

 

回滚服务的操作:

    假设转账超时时间是1小时;

    定期检查回滚库中的回滚日志表;

    如果事务号对应结束,则忽略;

    如果事务号没有结束,但是事务没超时,也忽略;

    如果事务号没有结束,事务超时,则按照回滚日志,反向操作,对事务进行补偿,补偿步骤如下:

        第1步:对用户2进行事务补偿,检查数据库2的用户2的事务是否成功;

        第2步:如果成功,则认为事务完成,在事务回滚日志表中将这次事务标识为成功;并跳到“结束步骤”;

        第3步:对用户1进行事务补偿,检查数据库1的用户1的事务是否成功;

        第4步:如果成功,则执行如下事务:

            开始事务

                update 用户表 set (用户1的余额) = (用户1的余额)+ 100;

                update 事务表 (事务号,回滚成功);

            结束事务

        第5步:在事务回滚日志表将这次事务标识为成功;

        结束步骤

 

 结束哈;

 

posted on 2018-11-11 21:59  聆听风琴的巴赫  阅读(7667)  评论(1编辑  收藏  举报

导航