分布式事务总结
GitHub地址
简介
- 什么是事务
- 事务是以一种可靠,一致的方式,访问和操作数据库中数据的程序单元
原则
- 原子性
- 一致性
- 隔离性
- 持久性
sql版的事务
- 案例教程:
http://www.runoob.com/mysql/mysql-transaction.html
1
2
3
4
5begin; # 开始事务
insert into runoob_transaction_test value(5);
insert into runoob_transaction_test value(6);
commit; # 提交事务
rollback; # 回滚
- 案例教程:
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();
}
- 事务的隔离级别
隔离级别 | 含义 |
---|---|
TransactionDefinition.ISOLATION_DEFAULT (默认) | 使用后端数据库默认的隔离级别。 |
TransactionDefinition.ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。 |
TransactionDefinition.ISOLATION_READ_COMMITTED | 允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。 |
TransactionDefinition.ISOLATION_REPEATABLE_READ | 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。 |
TransactionDefinition.ISOLATION_SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。 |
- 传播机制
传播行为 | 意义 |
---|---|
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
7public interface TransactionStatus extends SavepointManager{
boolean isNew Transaction();
boolean hasSavepoint();
void setRolbackOnly();
boolean isRollbackOnly();
boolean isCompleted();
}
PlatformTransactionManager的常见实现
- DataSourceTransactionManager
- JpaTransactionManager
- JmsTransactionManager
- JtaTransactionManager
Spring外部事务和JTA
本地事务
- Spring容器管理事务的生命周期
- 通过Spring事务接口调用
- 业务代码与具体事务的实现无关
外部(全局)事务
- 外部事务管理器提供事务管理
- 通过spring事务接口,调用外部管理器
- 使用JNDI等方式获取外部事务管理器的实例
- 外部事务管理器一般由应用服务器提供,如Jboss等
JTA
- 外部事务管理器提供JTA事务管理
- JTA事务管理器可以管理多个数据资源
- 通过2阶段提交实现多数据源的事务
- JTA用途:一个java服务访问多个数据源的时候
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事务异常
- Spring Boot 2.0 使用data JPA @Transactional 报错事务不回滚
- 解决方法:
https://blog.csdn.net/kylinregister/article/details/80678223
分布式系统
- 定义:将不同的组件分布在不同的服务器上,给用户提供一个可靠,统一的服务
基本原则
- C 一致性
- A 可用性
- P 分区容错性
分布式系统设计需要考虑的问题
- 服务拆分
- 数据拆分
- 计算拆分
- 服务状态以及异常处理
微服务架构的组成
- 服务发现与注册
- 服务网关与负载均衡
- 监控与熔断机制
- 配置,消息等
分布式事务
事务的原则
- A 原子性
- C 一致性
- I 隔离性
- D 持久性
spring分布式事务
spring JTA
- 可以使用如JBoss之类的应用服务器提供的JTA事务管理器
- 可以使用Atomikos,Bitronix等库提供的JTA事务管理器
不使用JTA的事务方案
使用MQ最大努力一次提交+重试
- 提交MQ事务出错,消息会被放回MQ,重新触发该方法
- 会重复数据库操作,需要忽略重复消息
链式事务管理
- 定义一个事务链
- 多个事务在一个事务管理器里依次提交
- 可能出错
如何选择方案
- 根据一致性要求
- 强一致性事务:JTA(性能最差,只适用于单个服务内)
- 弱,最终一致性事务:最大努力一次提交,链式事务(设计相应的错误处理机制)
- 根据场景
- 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: 通过定时任务检查超时订单,对未完成的订单做自动回滚
- 方法3: 保存出错消息,人工处理
事件溯源
- 优点
- 历史重新:从事件中重新生成视图数据库
- 方便的数据流处理与报告生成
- 性能
- 服务的松耦合
- 缺点
- 只能保证事务的最终一致性
- 设计和开发思维的转变,学习成本
- 事件结构的改变
- 扩展性: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\
- 可扩展性
- 分布式Command分发
- 通过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
- 表示某种业务动作
- 只被处理一次
- 可以有返回值
- 只做条件检查,触发相应Event去更新数据
- Event
- 表示系统内发生的事件,某种业务状态的更新
- 可以被多次处理
- 没有返回值
- 更新聚合数据并保持在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所在的服务可以实现分布式部署
分布式事务实现总结
- 保证高可用:网络,分布式部署
- 保证事务同步:同步多数据源的事务
- 保证幂等性:通过重试解决大部分错误
- 合理设计流程:条件检查,预留资源,业务操作,完成资源
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Chc-个人数据程序主页!