GitHub地址

分布式事务知识
SpringCloud整合

简介

  1. 什么是事务
  • 事务是以一种可靠,一致的方式,访问和操作数据库中数据的程序单元
  1. 原则

    • 原子性
    • 一致性
    • 隔离性
    • 持久性
  2. sql版的事务

    • 案例教程:http://www.runoob.com/mysql/mysql-transaction.html
      1
      2
      3
      4
      5
      begin;  # 开始事务
      insert into runoob_transaction_test value(5);
      insert into runoob_transaction_test value(6);
      commit; # 提交事务
      rollback; # 回滚
  3. mysql的查询锁

    1
    2
    3
    4
    5
    # 加入FOR UPDATE就会获取锁,但是会锁住全表
    SELECT * FROM T_USER FOR UPDATE

    # 加入where条件,则只会锁住符合条件的那条数据
    SELECT * FROM T_USER WHERE id=1 FOR UPDATE

Spring事务

spring事务机制

  • 提供统一的API接口支持不同的资源
  • 提供声明式事务管理
  • 方便的与Spring框架集成
  • 多个资源的事务管理,同步

spring事务抽象

  • PlatformTransactionManager:提供事务管理器的接口
    1
    2
    3
    4
    5
    6
    // 事务管理器
    public interface PlatformTransactionManager{
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status)throws TransactionException;
    void rollback(TransactionStatus status)throws TransactionException;
    }
  • TransactionDefinition: 事务的定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 事务定义
    public interface TransactionDefinition{
    // 获取事务传播机制
    int getPropagationBehavior();
    // 隔离级别
    int getlsolationLevel();
    // 事务名字
    String getName();
    // 超时时间
    int getTimeout();
    // 是否为只读
    boolean isReadOnly();
    }
  1. 事务的隔离级别
隔离级别 含义
TransactionDefinition.ISOLATION_DEFAULT (默认) 使用后端数据库默认的隔离级别。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。
TransactionDefinition.ISOLATION_READ_COMMITTED 允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。
TransactionDefinition.ISOLATION_REPEATABLE_READ 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
  1. 传播机制
传播行为 意义
TransactionDefinition.PROPAGATION_MANDATORY 表示该方法必须运行在一个事务中。如果当前没有事务正在发生,将抛出一个异常
TransactionDefinition.PROPAGATION_NESTED 表示如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。
TransactionDefinition.PROPAGATION_NEVER 表示当前的方法不应该在一个事务中运行。如果一个事务正在进行,则会抛出一个异常。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED 表示该方法不应该在一个事务中运行。如果一个现有事务正在进行中,它将在该方法的运行期间被挂起。
TransactionDefinition.PROPAGATION_SUPPORTS 表示当前方法不需要事务性上下文,但是如果有一个事务已经在运行的话,它也可以在这个事务里运行。
TransactionDefinition.PROPAGATION_REQUIRES_NEW 表示当前方法必须在它自己的事务里运行。一个新的事务将被启动,而且如果有一个现有事务在运行的话,则将在这个方法运行期间被挂起。
TransactionDefinition.PROPAGATION_REQUIRED (默认) 表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。
  • TransactionStatus: 正在运行的事务状态
    1
    2
    3
    4
    5
    6
    7
    public interface TransactionStatus extends SavepointManager{
    boolean isNew Transaction();
    boolean hasSavepoint();
    void setRolbackOnly();
    boolean isRollbackOnly();
    boolean isCompleted();
    }

PlatformTransactionManager的常见实现

  • DataSourceTransactionManager
  • JpaTransactionManager
  • JmsTransactionManager
  • JtaTransactionManager

Spring外部事务和JTA

本地事务

  • Spring容器管理事务的生命周期
  • 通过Spring事务接口调用
  • 业务代码与具体事务的实现无关
    image

外部(全局)事务

  • 外部事务管理器提供事务管理
  • 通过spring事务接口,调用外部管理器
  • 使用JNDI等方式获取外部事务管理器的实例
  • 外部事务管理器一般由应用服务器提供,如Jboss等

JTA

  • 外部事务管理器提供JTA事务管理
  • JTA事务管理器可以管理多个数据资源
  • 通过2阶段提交实现多数据源的事务
  • JTA用途:一个java服务访问多个数据源的时候
    image

XA与JTA

  • XA协议由Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准
  • Transaction Manager
  • XA Resource
  • 两阶段提交
  • Spring Boot通过Atomkos或Bitronix的内嵌事务管理器支持跨多个XA资源的分布式JTA事务,当部署到恰当的J2EE应用服务器时也会支持JTA事务。
  • 当发现JTA环境时,SpringBoot将使用Spring的JtaTransactionManager来管理事务。自动配置的JMS,DataSource和JPA beans将被升级以支持XA事务。你可以使用标准的Spring idioms,比如@Transactional,来参与到一个分布式事务中。如果处于JTA环境,但仍想使用本地事务,你可以将spring.jta.enabled属性设置为false来禁用JTA自动配置功能。

https://www.breakyizhan.com/springboot/3413.html

JTA事务管理的弊端

  • 两阶段提交(第一阶段:准备阶段(投票阶段)和第二阶段:提交阶段(执行阶段))
  • 事务时间太长,锁数据的时间太长
  • 低性能,低吞吐量

不使用JTA实现多数据源的事务管理

  • Spring事务同步机制
  • 多数据源上实现近似事务一致性
  • 高性能,高吞吐量

spring事务异常

  1. Spring Boot 2.0 使用data JPA @Transactional 报错事务不回滚
  • 解决方法:https://blog.csdn.net/kylinregister/article/details/80678223

分布式系统

  • 定义:将不同的组件分布在不同的服务器上,给用户提供一个可靠,统一的服务

基本原则

  • C 一致性
  • A 可用性
  • P 分区容错性

分布式系统设计需要考虑的问题

  • 服务拆分
  • 数据拆分
  • 计算拆分
  • 服务状态以及异常处理

微服务架构的组成

  • 服务发现与注册
  • 服务网关与负载均衡
  • 监控与熔断机制
  • 配置,消息等

分布式事务

事务的原则

  • A 原子性
  • C 一致性
  • I 隔离性
  • D 持久性

spring分布式事务

image

spring JTA

  • 可以使用如JBoss之类的应用服务器提供的JTA事务管理器
  • 可以使用Atomikos,Bitronix等库提供的JTA事务管理器

不使用JTA的事务方案

  1. 使用MQ最大努力一次提交+重试

    • 提交MQ事务出错,消息会被放回MQ,重新触发该方法
    • 会重复数据库操作,需要忽略重复消息
  2. 链式事务管理

    • 定义一个事务链
    • 多个事务在一个事务管理器里依次提交
    • 可能出错

如何选择方案

  1. 根据一致性要求
    • 强一致性事务:JTA(性能最差,只适用于单个服务内)
    • 弱,最终一致性事务:最大努力一次提交,链式事务(设计相应的错误处理机制)
  2. 根据场景
    • MQ-DB:最大努力一次提交+重试
    • 多个DB:链式事务
    • 多个数据源:链式事务,或其他事务同步方式

分布式事务实现模式与技术

分布式系统唯一性ID:GUID

  • 分布式系统的全局唯一标识
  • UUID:生成唯一id的规范
  • 用于唯一标识,处理重复消息
  • Redis的INCR操作,Zookeeper节点的版本号

分布式系统分布式对象

  • Redis:Redisson库:RLock,RMap,RQueue等对象
  • Zookeeper:Netflix Curator库:Lock,Queue等对象

实现模式

  • 消息驱动模式:Message Driven (使用MQ消息将服务串起来)
  • 事件溯源模式:Event Sourcing
  • TCC模式:Tty-Confirm-Cancel

消息驱动模式

微服务架构的事务问题

  1. 服务间调用操作的回滚
  2. 服务间调用失败的重试问题
  • 解决方法:
    • 方法1:减少服务间的调用
    • 方法2:没有服务间调用,通过消息驱动调用服务
  1. 案例地址:https://github.com/chcGolang/SpringCloud_Test

注意的问题

  • 消息中间件需要支持事务
  • 如何处理重试的消息
  • 发送业务异常时回滚操作

系统错误的处理

  • 方法1: 将出错未处理的消息写到失败队列,进行相应回滚操作
  • 方法2: 通过定时任务检查超时订单,对未完成的订单做自动回滚
  • 方法3: 保存出错消息,人工处理

事件溯源

  • 优点
    1. 历史重新:从事件中重新生成视图数据库
    2. 方便的数据流处理与报告生成
    3. 性能
    4. 服务的松耦合
  • 缺点
    1. 只能保证事务的最终一致性
    2. 设计和开发思维的转变,学习成本
    3. 事件结构的改变
    4. 扩展性:Event Store的分布式实现,事件的分布式处理

Axon框架

  • 学习入门地址:https://docs.axoniq.io/reference-guide/
  • 实现Event Sourcing和CQRS模式的框架
  • 实现命令,事件的分发,处理,聚合,查询,存储
  • 提供标签式开发,易维护,并提供SpringBoot的集成
  • 提供Command和Event
Axon框架的构成
  • 聚合:Aggregate
  • 聚合的资源库:Repository
  • Command:Command Bus和command handler
  • Event:Event Bus,Event Handler和Event Store
  • Saga:基于事件的流程管理模式
  • Query:执行数据查询操作的特殊Command\
  • 可扩展性
    1. 分布式Command分发
    2. 通过AMQP实现分布式Event分发和处理
Axon框架处理Command过程
  • Resource收到请求,send给CommandGateway
  • CommandGateway执行拦截器等,在发给CommandBus
  • CommandBus创建一个Unitofwork,关联一个事务,在其中调用CommandHandler处理这个Command
  • CommandHandler使用Repository获得一个聚合对象,并聚合所有该对象的event.设置lock,然后调用处理方法
  • CommandHandler在触发一个Event
Axon框架处理Event过程
  • CommandHandler执行apply来触发一个event
  • EventBus在这个event上执行拦截器等
  • EventBus将在这个event保存到EventStore
  • EventBus调用在这个event上注册的所有处理方法(在UnitOfWork中执行)
  • 在EventHandler中更新聚合数据,保存视图数据库,触发其他Command
Axon框架Command和event的区别
  • Command
    1. 表示某种业务动作
    2. 只被处理一次
    3. 可以有返回值
    4. 只做条件检查,触发相应Event去更新数据
  • Event
    1. 表示系统内发生的事件,某种业务状态的更新
    2. 可以被多次处理
    3. 没有返回值
    4. 更新聚合数据并保持在Event Store中,用重新生成聚合数据
Axon Saga
  • StartSaga - SageEventHandler - EndSaga
  • 使用associate将不同的事件关联到同一个Saga流程中
  • 正常的结束都通过EndSaga标签触发,超时使用EventScheduler,触发一个EndSaga
  • 一个业务流程的执行对应一个saga实例
  • Saga实例状态和关联的事件会保证在数据库中

Axon分布式Event设计

  • 聚合类上的Event Handler是本地处理,通过AMQP触发
  • 聚合类上是处理完Event,在通过AMQP分发
  • 每个服务监听各自的队列
  • 每个事件只会被处理一次
  • 每个EventHandler处理过的Event,都会保存在该服务的数据库中
  • Saga负责流程编排,Aggregate用于处理数据状态
  • Saga要负责处理流程内的所有事件
  • Saga处理的事件不会保存在domain_event表中
  • 每个saga对象也会序列化后保存在数据库中
  • saga所在的服务可以实现分布式部署

分布式事务实现总结

  • 保证高可用:网络,分布式部署
  • 保证事务同步:同步多数据源的事务
  • 保证幂等性:通过重试解决大部分错误
  • 合理设计流程:条件检查,预留资源,业务操作,完成资源