ACID和BASE这是事务实现的两种基础理论,
ACID是刚性事务,强调的是隔离性和强制一致性,隔离性的话就导致事务操作的资源在事务结束以前要一直被锁定占用,又因为强一致性,如果一个事务中包含了多个子事务,就导致了多个事务一次性完成,但是要是万一其中一个事务耗时很久,这样就会导致其他的事务一直占据着资源没法释放,从而影响整个系统的吞吐。
BASE是柔性事务,基本可用 可以保证分布式事务参与方不一定同时在线。柔性状态 允许系统状态更新有一定的延时,这个延时对客户来说不一定能察觉。最终一致性 通常是通过消息可达的方式保证系统的最终一致性。
什么叫XA?
XA是一种事务规范,主要是通过两阶段提交来完成分布式事务,两阶段提交基于ACID理论,这中间主要包括两个角色:资源管理器和事务管理器,
资源管理器主要负责我们的数据库等资源,
事务管理器就是一个中间件主要负责多个事物之间的协调,
场景如下:一次业务请求可能需要两个事务,
- 第一阶段——预提交:首先两个事务都会去操作自己的数据库,但是这次操作数据库并不会commit,也就是说不生效,只是试探性的看看这次操作能否成功,无论成功与否,如果成功的话将会记录此次的操作记录,并一直锁定要操作的数据资源,返回结果给事务管理器,如果失败则直接回滚所做的操作,并立即释放锁定的数据资源,也将结果返回给事务管理器。
- 在第二阶段:交易中间件事务管理器收到所有的事务预操作返回结果,并审查所有数据库返回的预提交结果,如所有数据库都可以提交,交易中间件将要求所有数据库做正式提交,这样该全局事务被提交。而如果有任一数据库预提交返回失败,交易中间件将要求所有其它数据库回滚其操作,这样该全局事务被回滚。
XA数据源也就是支持XA事务的数据源。
什么是TCC?
TCC是一种分布式事务解决方案:Try-Confirm-Cancel,TCC是基于BASE理论,
- Try:尝试执行业务,完成所有业务的检查,预留必须的业务资源
- Confirm:确认执行业务,真正的执行业务,不做业务检查
- Cancel:取消执行业务,释放Try阶段预留的业务资源
上面的解释可能不太容易明白:举个例子,你有女朋友,你在某宝要买100包杜蕾斯,这个流程是什么呢?
Try阶段:业务先去检查库存还有多少,加入还有1000 然后减去100(被记录下来) 这个时候库存剩下900
Confirm阶段:根据Try阶段的结果,如果满足100个,也就是操作生效,这个时候业务Try阶段减得的库存就直接生效了
CanCel阶段:如果Try阶段发现不满100个,这个时候就根据操作记录回滚,将库存再加上100(这个是在Try阶段被记录了的)就行了。
什么是Saga?
Saga其实是30年前的一篇数据库论文里提到的一个概念。在论文中一个Saga事务就是一个长期运行的事务,这个事务是由多个本地事务所组成, 每个本地事务有相应的执行模块和补偿模块,当saga事务中的任意一个本地事务出错了, 可以通过调用相关事务对应的补偿方法恢复,达到事务的最终一致性。
Saga对服务的要求:幂等性和补偿可交换原则
因为可能因为网络问题,会导致出现超时重试的情况,这样就需要服务支持幂等性,避免多次请求带来的问题。
重试取消的情况。补偿可交换原则是指Saga并行处理的过程中,如果发生了超时重试事件之后,并进行了补偿的操作,那么补偿操作是直接生效的。为了满足这个要求,需要我们在设计系统的过程中保留所有的事务数据
ACID和Saga
从上面我们可以看出,Saga只支持ACD,不提供隔离性保证。
缺乏隔离性会给我们带来以下问题:
- 两个Saga事务同事操作一个资源会出现数据语义不一致的情况
- 两个Saga同事操作一个订单,彼此操作会覆盖对方(更新丢失)
- 两个Saga事务同时访问扣款账号,无法看到退款(脏读取问题)
- 在一个Saga事务内,数据被其他事务修改前后的读取值不一致(模糊读取问题)
如何应对隔离问题:
- 隔离的本质就是控制并发防止并发事务操作相同资源而引起结果错乱
- 在应用层面加入逻辑锁的逻辑
- Session层面隔离来保证串行化操作
- 业务层面采用预先冻结资金的方式来隔离此部分资金
- 业务操作过程中通过及时读取当前的状态的方式来获取更新
Saga的两种实现方式:
集中式的实现方式:集中式协调器负责服务调用以及事务协调
分布式的实现方式:通过事件驱动的方式进行事务的协调
集中式的Saga实现一般是通过一个Saga对象来追踪所有的Saga子任务的调用情况, 根据调用情况来决定是否需要调用对应的补偿方面,协调器和调用方是在一个进程中的。缺点就是耦合度太高。
分布式的Saga的实现使用过事件驱动的,相关的服务监听相关的事件,从而触发该服务,因此分布式Saga也叫事务编排
分布式事务的好处是:降低了耦合性以及系统的复杂性,系统的逻辑处理是基于事件。
举个具体的例子:我们现在的系统中就是基于分布式Saga处理的,我们是通过Kafka作为消息中间件,
每个服务订阅相关的Channel,当捕获相关的事件之后就自动触发服务。这里面最重要的是要持久化相关的环节,比如持久化事件信息以及中间信息,因为事务出现回滚或者补偿的时候会需要这些信息。
微服务事务的一致性建议:内刚外柔
内刚:在微服务内部通过数据库事务保证强一致性
外柔:在微服务之间的保证服务的最终一致性
参考链接:https://servicecomb.apache.org/cn/docs/distributed-transactions-saga-implementation/