几种可行的分布式事务解决方案

数据库层面上的分布式事务

XA事务:是DTP模型定义TM和RM之间通讯的接口规范。XA接口函数由数据库厂商提供。TM用它来通知数据库事务的开始、结束、提交、回滚。基于XA规范衍生出下面的二阶段提交(2PC)、三阶段提交(3PC),sharding-jdbc和mycat都实现了XA事务

JTA:Java的事务API,需要第三方实现,目前JTA的实现有以下几种形式

  1. J2EE容器提供的JTA实现(Weblogic、JBoss );
  2. JOTM(Java Open Transaction Manager)、Atomikos,可独立于J2EE容器的环境下实现JTA

业务层面的事务解决方案

1、seata

角色划分:

  1. RM(ResourceManager 资源管理者)理解为 我们的一个一个的微服务 也叫做事务的参与者.
  2. TM(TranactionManager 事务管理者) 也是我们的一个微服务,但是该微服务是一个带头大哥,充当全局事务的发起者(决定了全局事务的开启,回滚,提交等),凡是我们的微服务中标注了@GlobalTransactional ,那么该微服务就会被看出一个TM。我们业务场景中订单微服务就是一个事务发起者,同时也是一个RM
  3. TC(全局事务的协调者):这里就是我们的Seata-server,用来保存全局事务,分支事务,全局锁等记录,然后会通知各个RM进行回滚或者提交.

AT模式:

当执行带有@GlobalTransactional注解的方法的时候,就会被认为是一个分布式事务,就会向TC(seata server)注册一个分布式全局事务,即往全局事务表中插入一条数据

执行分支逻辑,如:update product set name = 'GTS' where name = 'TXC';

    一阶段:

  1. 获取前置镜像和后置镜像,生成插入undo日志表的sql,让插入undo日志表的sql和分支逻辑sql放在一起进行提交
  2. 在提交前,向TC(seata server)注册一个分支事务,即往分支事务表中插入有一条数据,还有要将分支逻辑sql受影响的数据添加到TC(seata server)的全局锁表中
  3. 提交之后将执行结果(成功和失败)上报给分支事务

    二阶段-回滚: 

      收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。

  1. 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
  2. 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
  3. 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:update product set name = 'TXC' where id = 1;
  4. 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

      二阶段-提交:

  1. 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果
  2. 给 TC。异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录

缺点:隔离级别为读未提交,如果要实现可重复读,则select ... from for update,所以要保证数据强一致性,则性能会很慢

TCC模式:

根据两阶段行为模式的不同,我们将分支事务划分为 Automatic (Branch) Transaction Mode 和 Manual (Branch) Transaction Mode.

AT 模式(参考链接 TBD)基于 支持本地 ACID 事务 的 关系型数据库

  • 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
  • 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
  • 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。

相应的,TCC 模式,不依赖于底层数据资源的事务支持:

  • 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
  • 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
  • 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。

所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

2、消息队列rocketmq

事务消息模式:

 TransactionListener transactionListener = new OrderTransactionListenerImpl();
        //初始化生产者
        TransactionMQProducer producer = new TransactionMQProducer("TxProducer");
        producer.setTransactionListener(transactionListener);
        producer.setNamesrvAddr("127.0.0.1:9876");
        producer.setInstanceName("tx-instance");
        producer.start();

        try {
            Object msg = new Object();
            Message message = new Message("order-topic", JSON.toJSONString(msg).getBytes());
            TransactionSendResult sendResult = producer.sendMessageInTransaction(message, null);
            System.out.println(sendResult);
        }
        catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdown();
        }
        return false;

@SuppressWarnings("unused")
public class OrderTransactionListenerImpl implements TransactionListener {

    private ConcurrentHashMap<String, Integer> countHashMap = new ConcurrentHashMap<>();

    private final static int MAX_COUNT = 5;


    @SneakyThrows
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        Connection connection = null;
        connection.setAutoCommit(false);
        try {
           //执行本地业务逻辑
            connection.commit();
        }catch (Exception e){
            connection.rollback();
            return LocalTransactionState.UNKNOW;
        }
        return LocalTransactionState.COMMIT_MESSAGE;
    }
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        try {
            //回查成功,这里主要是本地事务运行成功了,但是生产者向broker发送事务确认由于网络原因,
            // 没有发送成功,则broker中的消息状态一直是half状态,则等网络恢复后,提供broker调用的回查机制
        }catch (Exception e){
            return LocalTransactionState.UNKNOW;
        }
        return LocalTransactionState.COMMIT_MESSAGE;
    }
}

 rokectmq事务消息可以保证数据库写入和mq消息发送保持一致性

非事务消息模式:

        Connection connection = null;
        connection.setAutoCommit(false);
        try {
           //执行本地业务逻辑
           //插入需要发送给mq的数据到t_message_transaction表中
            connection.commit();
        }catch (Exception e){
            connection.rollback();
            return LocalTransactionState.UNKNOW;
        }

 当本地事务执行成功,则插入需要发送给mq的数据到t_message_transaction表中,然后后台用一个定时任务,把t_message_transaction表中的数据发送给mq,注意:rocket mq必须要保证数据不被丢失,消费端必须一定要具备幂等性,否则会造成数据不一致

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值