深入了解分布式.md

落花浮王杯 提交于 2020-02-21 18:57:17

分布式事务

分布式事务概念

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。分布式事务是为了保证不同数据库的数据一致性

分布式事务产生的原因

  • 数据库分库分表
    当数据库单表一年产生的数据超过1000W,那么就要考虑分库分表

  • 应用SOA化
    所谓的SOA化,就是业务的服务化。现在对整个网站进行拆解,分离除了订单中心、用户中心、库存中心。

事务的ACID特性

  • 原子性(Atomicity)

    所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。

  • 一致性(Consistency)

    事务的执行必须保证系统的一致性,就拿转账为例,A有500元,B有300元,如果在一个事务里A成功转给B50元,那么不管并发多少,不管发生什么,只要事务执行成功了,那么最后A账户一定是450元,B账户一定是350元。

  • 隔离性(Isolation)

    所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。

  • 持久性(Durability)

    所谓的持久性,就是说一单事务完成了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。

分布式理论

CAP理论

  • 一个分布式系统最多只能同时满足两项

    1. 一致性(Consistency)

    2. 可用性(Availability)

    3. 分区容错性(Partition tolerance)

BASE理论

  • BASE理论是对CAP理论的延伸,思想是即使无法做到强一致性,但可以适当的采用弱一致性,即最终一致性

    1. 基本可用(Basically Available)

    2. 软状态(Soft State)

    3. 最终一致性(Eventual Consistency)

分布式事务的应用场景

  • 支付

  • 在线下单

常见的分布式事务解决方案

  • 基于XA协议的两阶段提交

    XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。

    总的来说,XA协议比较简单,而且一旦商业数据实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,那就是性能不理想,特别是在交易下单链路,往往并发量很高,XA无法满足高并发场景。XA目前在商业数据库支持的比较理想,在mysql数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换会导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘

  • 消息事务+最终一致性

    所谓的消息事务就是基于消息中间件的两个阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功,并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性

两阶段提交

  • 概念

    事务会进行两次提交,这里介绍两个概念

    1. 一个是事务协调者(事务管理器Transaction Manager):用来协调事务,所有事务什么时候准备好了,什么时候可以提交了,都由它来协调和管理

    2. 另外一个是参与者(资源管理器Resource Manager):主要负责处理具体事务的,管理者需要处理的资源。如:订票业务,扣款业务

  • 第一阶段(准备阶段)

    事务协调者给每个参与者发送Prepare消息,参与者会根据自身业务和资源情况进行检查,然后给出反馈。

    例如:订票业务,就要检查是否有剩余票。扣款业务就要检查,余额是否足够。一旦检查通过后,才能返回就绪(Ready)信息

    否则,事务将终止,并且等待下次询问。由于这些检查需要做一些操作,这些操作可能在之后回滚时用到,所以需要写redo和undo日志,当事务失败重试,或者事务失败回滚时候使用

  • 第二阶段(提交阶段)

    如果协调者收到了参与者失败或者超时的消息,会给参与者发送回滚(rollback)消息;否则,发送提交(commit)消息

  • JTA(满足XA规范,用于Java开发)

    
      <!-- 引入jta依赖 -->
      <dependency>
          <groupId>javax.transaction</groupId>
          <artifactId>jta</artifactId>
          <version>1.1</version>
      </dependency>
    
      <dependency>
          <groupId>javax.transaction</groupId>
          <artifactId>javax.transaction-api</artifactId>
          <version>1.2</version>
      </dependency>
    
    
  • 其他

    XA协议需要jdbc和数据库都支持

    MySQL从5.0.3开始支持XA分布式事务,且只有InnoDB存储引擎支持

TCC编程模式

  • 例图

    两阶段提交和TCC编程模式对比

  • 简介

    所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。

    以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。

    总之,TCC就是通过代码人为实现了两阶段提交,**其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务,**因此,这种模式并不能很好地被复用。

  • 概念

    TCC是一种补偿性分布式事务解决方案,最初由支付宝提出。TCC是三个英文单词首字母缩写,分别对应Try、Confirm和Cancel三种操作,这三种操作的业务含义如下:

    Try:预留业务资源
    
    Confirm:确认执行业务操作  
    
    Cancel:取消执行业务操作
    

    关系型数据库事务的三种操作:DML、Commit和Rollback,会发现和TCC有异曲同工之妙。

    在一个跨应用的业务操作中,
    Try操作是先把多个应用中的业务资源预留和锁定住,为后续的确认打下基础,类似的DML操作要锁定数据库记录行,持有数据库资源;

    Confirm操作是在Try操作中涉及的所有应用均成功之后进行确认,使用预留的业务资源,和Commit类似;

    Cancel则是当Try操作中涉及的所有应用没有全部成功,需要将已成功的应用进行取消(即Rollback回滚)。其中Confirm和Cancel操作是一对反向业务操作

  • 总结

    简而言之,TCC是应用层的2PC(2 Phase Commit,两阶段提交),如果你将应用看做资源管理器的话。详细来说,TCC每项操作需要做的事情如下:

    1. Try:尝试执行业务
      完成所有业务检查(一致性)
      预留必须业务资源(准隔离性)

    2. Confirm:确认执行业务
      真正执行业务
      不做任何业务检查
      只使用Try阶段预留的业务资源

    3. Cancel:取消执行业务
      释放Try阶段预留的业务资源

  • 幂等性

    幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。

TCC开源框架-tcc-transaction

  • 框架简介

    tcc-transaction是开源的TCC补偿性分布式事务架构

    Git 地址: https://github.com/changmingxie/tcc-transaction

  • 安装tcc-tansaction到本地仓库

    引入tcc-transaction工程1.2版本

    安装tcc-transaction-api、tcc-transaction-core和tcc-transaction-dubbo到本地仓库

TCC使用关键技术分析

  • AOP切面

    事务发起者、参与者的方法都需要经过切面处理,可根据Spring实现事务注解的思路分析

  • 反射

    在事务第二阶段使用反射技术调用Confirm、Cancel方法

  • 持久化

    所有参与事务的服务都参与持久化数据的流转

  • 序列化
    Kryo、JdkSerializer

  • 定时任务
    quartz事务恢复

  • 动态代理

    对dubbo模块的支持,结合dubbo的SPI扩展dubbo代理方式

分布式项目使用tcc-transaction框架

  • 框架环境搭建

    1. pom.xml中引入依赖

      
          <dependency>
              <groupId>org.mengyun</groupId>
              <artifactId>tcc-transaction-dubbo-capital-api</artifactId>
              <version>1.2.4.14</version>
          </dependency>
          <dependency>
              <groupId>org.mengyun</groupId>
              <artifactId>tcc-transaction-dubbo</artifactId>
          <version>1.2.4.14</version>
          </dependency>
          <dependency>
              <groupId>org.mengyun</groupId>
              <artifactId>tcc-transaction-spring</artifactId>
              <version>1.2.4.14</version>
          </dependency>
      
      
      
    2. web.xml上下文加载tcc-transaction

      
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath*:config/spring/local/appcontext-*.xml,classpath:tcc-transaction.xml,classpath:tcc-transaction-dubbo.xml
              </param-value>
          </context-param>
      
      

发布服务

  • 发布一个Tcc服务方法,可被远程调用并参与到TCC事务中,四个约束:

    1. 在服务提供方的实现方法上加上@Compensable注解,并设置注解的属性

    2. 在服务提供方的接口方法上加上@Compensable注解

    3. 服务方法的入参能被序列化(默认使用jdk序列化机制,需要参数实现Serializable接口,可以设置repository的serializer属性自定义序列化实现)

    4. try方法、confirm方法和cancel方法入参类型须一样

    Compensable的属性包括propagation、confirmMethod、cancelMethod、transactionContextEditor。

    (1)propagation可不用设置,框架使用缺省值;
    (2)设置confirmMethod指定confirm阶段的调用方法;
    (3)设置cancelMethod指定cancel阶段的调用方法;
    (4)设置transactionContextEditor为DubboTransactionContextEditor.class
    
  • 发布TCC服务示例

    1. try接口方法

      
          @Compensable
          public String record(CapitalTradeOrderDto tradeOrderDto);
      
      
    2. try实现方法

      
          @Compensable(confirmMethod = "confirmRecord", cancelMethod="cancelRecord", transactionContextEditor=DubboTransactionContextEditor.class)
          public String record(CapitalTradeOrderDto tradeOrderDto){
      
          }
      
      
    3. confirm方法

      
          public void confirmRecord(CapitalTradeOrderDto tradeOrderDto){
      
          }
      
      
    4. cancel方法

      
          public void cancelRecord(CapitalTradeOrderDto tradeOrderDto){
      
          }
      
      

调用服务

  • 调用远程TCC服务,将远程TCC服务参与到本地TCC事务中,本地的服务方法也需要声明为TCC服务,有三个约束:

    1. 在服务方法上加上@Compensable注解,并设置注解属性

    2. 服务方法的入参都须能序列化(实现Serializable接口)

    3. try方法、confirm方法和cancel方法入参类型须一样

  • 调用Tcc服务示例

    1. try方法

      
          @Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment")
          public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount){
      
              System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
              order.pay(redPacketPayAmount, capitalPayAmount);
              orderRepository.updateOrder(order);
              String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));
              String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));
      
      
          }
      
      
    2. confirm方法

      
          public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount){
      
          }
      
      
    3. cancel方法

      
          public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount){
      
          }
      
      

LCN解决方案

  • TX-LCN核心控制流程

    LCN处理分布式事务原理图

  • 原理介绍

    LCN模式是通过代理Connection的方式实现对本地事务的操作,然后再由TxManager(独立服务)统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。

  • 模式特性

    1. 仅限于本地存在连接对象且可通过连接对象控制事务的模块

    2. 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。

    3. 缺陷在于代理的连接需要随事务发起方一同释放连接,增加了连接占用的时间

参考链接

  • 深入理解分布式事务
    http://www.codeceo.com/article/distributed-transaction.html

  • atomikos JTA/XA全局事务
    http://www.tianshouzhi.com/api/tutorials/distributed_transaction/386

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!