【漫谈】跨库事务控制方案

5 篇文章 0 订阅
3 篇文章 0 订阅

很多时候我们都会接触到跨库事务的问题,本篇就以常说的跨行转账作为例子来提供一种方案,供大家参考。

    首先,我们知道跨库的话,数据库层级是做不到事务控制的,只能通过逻辑层实现事务控制,那么本篇文章就给大家提供一种方案:利用中间表实现跨库事务控制。

    先大致讲述下方案:

  1. 新建中间库,在用户调用转账逻辑时,生成唯一凭证,此凭证作为接口幂等依据,入库一条数据,状态为待处理,并通知用户转账请求已提交,请等待处理,此处不涉及跨库。
  2. 新建定时任务,从中间库中查出所有状态为非SUCCESS的数据,根据各个状态做不同处理,比如待冻结则调用银行A,即A库执行对应冻结金额逻辑,之后更新中间表状态。此处涉及跨库,可能出现中间表状态未更新情况,可通过接口幂等和对账解决。
  3. 对账,每天执行一次,用于银行A/B与中间表对账,防止出现状态未变更导致后续处理未进行状态(出现问题可能性较小)。

    下面直接来看伪代码,此处省去第一步中间表入库动作,直接来看定时跑批流程:

/**
* 先定义状态枚举,下面一个方法会用到
*/
enum TransferStatus {
                     WAITTING_FREEZE("待扣款"), WAITTING_REMIT("待打款"), WAITTING_WITHHOLD("待扣款"), SUCCESS("转账成功");
    private String message;

    TransferStatus(String message) {
        this.message = message;
    }

    public static TransferStatus getByName(String name) {
        for (TransferStatus status : TransferStatus.values()) {
            if (status.name().equals(name)) {
                return status;
            }
        }
        return null;
    }
}

/**
* 核心方法,定时跑批调用,根据业务需求决定跑批频率
*/
public void handler() {
    TransferStatus status = TransferStatus.getByName("从数据库中查出的状态");
    String voucherNO = "从库中查出的voucherNO";
    switch (status) {
        case WAITTING_FREEZE:
            /**************************************************************************************************************
             *  此处调用银行A接口,执行:
             *  1:插入交易记录(voucherNO作为唯一键,保证接口幂等):
             *  2:预扣款(冻结扣款金额,防止用户继续消费这些金额,但是暂时未实际扣除)
             *  上面两步为同库操作,可直接用事务控制
             *  成功后更新本条记录状态为WAITTING_REMIT  可选通知银行B打款  失败则走扣款失败逻辑。
             *  注意:此处可能出现通知银行A成功,但是此时数据库故障,本地记录状态未更新    这种情况通过如下步骤解决:
             *  1)状态未更新下次跑批处理依旧是此状态,则继续执行上述所有步骤,但voucherNO唯一,且银行A接口幂等,则直接会成功,并重新更新本记录状态
             *  2)依旧没有成功,则通过定时与银行的对账来解决
             **************************************************************************************************************/
            break;
        case WAITTING_REMIT:
            /**************************************************************************************************************
             *  此处调用银行B接口,执行:
             *  1:插入交易记录(voucherNO作为唯一键,保证接口幂等)
             *  2::打款
             *  上面两步为同库操作,可直接用事务控制
             *  成功后更新本条记录状态为WAITTING_WITHHOLD  并可选通知用A银行扣款,并通知用户到账。      失败则通知银行A回滚
             *  注意:此处可能出现通知银行B成功,但是此时数据库故障,本地记录状态未更新    这种情况通过对账解决
             *  1)状态未更新下次跑批处理依旧是此状态,则继续执行上述所有步骤,但voucherNO唯一,且银行B接口幂等,则直接会成功,并重新更新本记录状态
             *  2)依旧没有成功,则通过定时与银行的对账来解决
             **************************************************************************************************************/
            break;
        case WAITTING_WITHHOLD:
            /**************************************************************************************************************
             *  此处调用银行A接口,执行正式扣款
             *  成功后更新本条记录状态为SUCCESS  并通知用户转账成功
             **************************************************************************************************************/
            break;
        case SUCCESS:
        default:
            break;
    }
}

    上面解释已经很详细了,这里就不多说,其中有很小几率出现总是不成功,则通过对账解决:

/**
* 对账方法      
*/

public void checkAccount () {
    /**************************************************************************************************************
     *  每天1点执行,查询前一天天交易记录,发送voucherNO和状态给中间系统,中间系统根据数据更新记录状态
     **************************************************************************************************************/
}

总结:跨库不能通过数据库控制事务,只有通过逻辑层来控制


欢迎关注个人博客:blog.scarlettbai.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值