分布式事物介绍

本地事务概念

在这里插入图片描述

由一组家族内操作构成的可靠、独立的工作单元
  • ACID:
    • Atomicity(原子性)
    • Consistency(一致性)
    • Isolation(隔离性) ----消除幻读
    • Durability(持久性)

分布式理论

当我们的单个数据库的性能产生瓶颈的时候,我们可能会对数据库进行分区,这里所说的分区指的是物理分区,分区之后可能不同的库就处于不同的服务器上了,这个时候单个数据库的ACID已经不能适应这种情况了,而在这种ACID的集群环境下,再想保证集群的ACID几乎是很难达到,或者即使能达到那么效率和性能会大幅下降,最为关键的是再很难扩展新的分区了,这个时候如果再追求集群的ACID会导致我们的系统变得很差,这时我们就需要引入一个新的理论原则来适应这种集群的情况,就是 CAP 原则或者叫CAP定理,那么CAP定理指的是什么呢?

CAP定理

CAP定理是由加州大学伯克利分校Eric Brewer教授提出来的,他指出WEB服务无法同时满足一下3个属性:

一致性(Consistency) : 客户端知道一系列的操作都会同时发生(生效)
可用性(Availability) : 每个操作都必须以可预期的响应结束
分区容错性(Partition tolerance) : 即使出现单个组件无法可用,操作依然可以完成

具体地讲在分布式系统中,在任何数据库设计中,一个Web应用至多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。

分布式事务概念

在这里插入图片描述

由一组跨家族的操作构成的可靠、独立的工作单元
  • 难点
    • 高度并发
    • 资源分布
    • 大时间跨度

分布式事务解决方法

1.两阶段提交(2PC)

XA规范下的2pc事务

在这里插入图片描述

@Transactional
    public String doOrder(String busId,String idcard) {
        System.out.println("begin.....");

        String sql = "UPDATE tcc_fly_order SET bus_id=?,STATUS = 1,idcard=?,remark=? WHERE STATUS = 0 LIMIT 1";

        //锁定订单
        int ret = ghJdbcTemplate.update(sql,busId,idcard,"国航");
        if (ret != 1){
            throw new RuntimeException("国航下单失败,无票可订");
        }

        //锁定订单
        ret = nhJdbcTemplate.update(sql,busId,idcard,"南航");
        if (ret != 1){
            throw new RuntimeException("南航下单失败,无票可订");
        }
        System.out.println("end.....");
        return "success";
    }

只要doOrder方法里抛出了RuntimeException,则两个数据库里的sql结果,都会被回滚掉

原理:先操作服务1,再才做服务2,都成功则全部提交,否则全部回滚,所使用的是两阶段提交方法

两阶段提交原理图

在这里插入图片描述
两阶段提交协议(Two-phase commit protocol)是XA用于在全局事务中协调多个资源的机制。

TM和RM间采取两阶段提交(Two-phase Commit)的方案来解决一致性问题。

两阶段提交需要一个协调者(TM)来掌控所有参与者节点(RM)的操作结果并且指引这些节点是否需要最终提交。

2PC事务解决方案的利弊

  • 优点:
    • 严格的ACID
       
  • 缺点:
    • 效率非常低(微服务架构下已不太适用)
    • 全局事务方式下,全局事务管理器(TM)通过XA接口使用二阶段提交协议(2PC)与资源层(如数据库)进行交互。使用全局事务,同时锁定两个数据库的数据,直到全局事务结束,事务锁定时间大大延长。
    • 与本地事务相比,XA协议的系统开销相当大,因而应当慎重考虑是否确实需要分布式事务,只有支持XA协议的资源才能参与分布式事务

       

2.补偿事务(TCC)

base理论/柔性事务 ----- 为了可用性,牺牲一致性

在这里插入图片描述

  • base理论的本质,是不再遵守ACID标准,不做强一致性
  • base理论的S,即指事务执行的中间状态(commit之前的状态),可以不隔离锁定,允许对外界表现出不一致
    -----------一个事务执行需要时间 ------- a步骤—》b步骤 -------- 20毫秒 ---- 中间状态
  • base理论的E,即指保证最终所有的步骤都得到执行,达到最终数据是一致的
整个base理论的过程,如下:

事务开始前 ------ 数据是一致的
事务进行中 ------ 数据是不一致的 ---- 中间态不一致 ---- 放宽标准
事务结束后 ------ 数据又一致

  • 对比ACID事务,base事务就是砍掉其中的数据锁定状态
    ----------------为了可用性 — 数据锁定的状态— 不可用状态
  • 业界对事务分类标准:
    ACID标准,强一致性要求的事务标准 ----- 刚性事务
    牺牲一致,放弃ACID里的强一致要求-------柔性事务:
     

tcc补偿性事务流程图

在这里插入图片描述

tcc补偿性事务示例讲解

准备工作

如果你要玩儿 TCC 分布式事务,必须引入一款 TCC 分布式事务框架,比如国内开源的 ByteTCC、Himly、TCC-transaction。

否则的话,感知各个阶段的执行情况以及推进执行下一个阶段的这些事情,不太可能自己手写实现,太复杂了

tcc补偿性事务流程分析
  • 一个普通业务接口-----切成三个接口:try–confirm–cancel
    • try ------- 将原业务接口,涉及到的资源锁定/或者做预备 ------ 通过一个冻结状态,做排它处理
    • confirm ------ 正式调用原业务接口方法,如:insert一条数据1 ----- 因try阶段已经为此语句准备好了执行条件,不会再出现业务的冲突情况(业务一定是可行的)
    • cancel -------- 恢复资源状态,取消资源的冻结状态
       
这里以在南航和国航个买往返票为例
  • 第一阶段:try

    • 锁定资源 改一下资源状态

      案例中为 锁定南航,国航各一张票的资源,更具需求在数据表中加一个frozen字段
	//try方法,busId:生成的唯一订单号,idcard:身份证号
    public int doOrder(String busId,String idcard) {

        System.out.println("frozen order:"+busId);

        //幂等性要求,如果订单号已存在,则直接返回,保证只锁定1条数据
        Integer count = jdbcTemplate.queryForObject("SELECT COUNT(1) FROM tcc_fly_order t WHERE t.bus_id = ?",Integer.class,busId);
        if (count > 0){
            return 1;
        }

        //锁定国航订单 设置1条数据frozen = 1,订单号为当前订单号
        int ret = jdbcTemplate.update(
                "UPDATE tcc_fly_order SET frozen = 1,bus_id=? WHERE STATUS = 0 AND frozen=0 LIMIT 1",busId);
        if (ret != 1){
            throw new RuntimeException("下单失败,国航无票可订");
        }
        //锁定南航订单,道理与锁定国航订单相同
        nhService.doOrder(busId,idcard);
        return ret;
    }

 

  • 第二阶段:confirm
    • try阶段已经为此语句准备好了执行条件,不会再出现业务的冲突情况(业务一定是可行的)

    • confirm 的时候会查询try里面的操作是否成功

      如果你在各个服务里引入了一个 TCC 分布式事务的框架,订单服务里内嵌的那个 TCC 分布式事务框架可以感 知到,各个服务的 Try 操作都成功了。此时,TCC 分布式事务框架会控制进入 TCC 下一个阶段,也就是 Confirm 阶段。

      案例中为查询南航,国航资源是否都成功锁定
      如果都成功则执行数据修改nhService.confirmOrder和ghservice.confirmOrder

	//分别调用 
    public int confirmOrder(String busId,String idcard) {
        System.out.println("confirm order:"+busId);

        //本语句是幂等性的
        int ret = jdbcTemplate.update("UPDATE tcc_fly_order SET frozen = 0,STATUS = 1,idcard=? WHERE bus_id=? and STATUS = 0 ",idcard,busId);
        return ret;
    }
  • 第三阶段:cancel
    • 恢复资源状态,取消资源的冻结状态

      如果有Try 操作执行失败,那订单服务内的 TCC 事务框架是可以感知到的,然后它会决定对整个 TCC 分布式事务进行回滚。

      案例中为查询南航,国航资源时发现资源锁定失败
      则解冻南航,国航被都冻结的资源nhService.cancelOrder和ghservice.cancelOrder

    public int cancelOrder(String busId,String idcard) {//补偿性
        System.out.println("cancel order:"+busId);

        //本语句是幂等性的
        int ret = jdbcTemplate.update("UPDATE tcc_fly_order SET frozen = 0,STATUS = 0 WHERE bus_id=?",busId);
        return ret;
    }
}

try-confirm-cancel的三个步骤联动过程

  1. 如果try不出问题正常结束,则框架自动去调用confirm方法
  2. 如果try出了异常,框架去调用cancel补偿方法

tcc框架对业务方法的要求

  1. 可查询操作:操作具有唯一标识,可查询操作执行结果
  2. 幂等操作: y = f(x) = f(f(x)) = f(f(f(x)))
  3. TCC操作 : Try-Confirm-Cancel
  4. 补偿操作 : 抵销(或部分抵销)正向业务操作的业务结果

tcc是如何解决2PC的问题呢?

Try操作主要是为了“保证业务操作的前置条件都得到满足”,然后在Confirm/Cancel阶段,因为前置条件都满足了,所以可以不断重试保证成功。这就要求Confirm/Cancel都必须是幂等操作。

 
关于tcc介绍,介绍一篇很不错的博客:https://www.cnblogs.com/jajian/p/10014145.html

3.其它事务框架

异步确保型事务
-------- 典型的使用MQ中间件来传递事务调用。
-------- 其重点是确保事务消息一定会传达到目标系统,目标系统只许成功不许失败(不成功我也不管)
-------- 因为是异步的,因此事务发起后,就不再关注了。名曰事务,已不像事务范畴

最大努力通知型
-------- 通过多线程异步重试,来传递事务调用
-------- 即通过重试机制,努力将事务调用传递到目标系统,目标系统只许成功不许失败(不成功我也不管)
-------- 因为是异步的,其执行已经脱离事务范畴

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值