分布式事务
事务的四大特性:
- 原子性Atomicity: 一个事务的所有操作,要么全部完成,要么全部失败。事务在执行过程中发生错误,会被回滚到事务开始前的状态。
- 一致性Consistency: 一个事务执行后,数据库的数据必须符合所有的规则与要求。数据库系统通过各种机制来确保一致性,并会在违反一致性时回滚事务,防止数据损坏。(一致性是事务的最终目标,原子性、隔离性和持久性是实现一致性的手段)
- 隔离性Isolation: 数据库允许多个并发事务,同时对数据库的数据进行读写,防止并发事务互相干扰
- 持久性Durability: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会。(只要不对他额外地进行删除和修改,这个数据永远不会变化)
强一致性和最终一致性
分布式事务地主要解决方案,就是基于强一致性和最终一致性
强一致性就是在任何时候,数据库的数据都必须要符合所有的规则和要求,而最终一致性允许在一段时间内数据不一致的情况,但是最终还是要实现一致性。也就是在不一致和一致之间有一个时间窗口,在这个窗口期可以不一致。
XA协议的2PC和3PC
2PC
它引入了一个事务协调者角色,来管理各个微服务,每一个微服务就是一个事务参与者。2PC有两个阶段:准备阶段和提交/回滚阶段
第一阶段:事务协调者发送一个准备命令,每一个事务参与者收到命令后,就会执行事务相关操作,但是不提交事务(除了提交事务之外,所有事情都做了),然后参与者返回响应,告诉协调者是否准备成功。
第二阶段:协调者根据所有参与者的响应结果,如果所有参与者响应的是准备成功,就发送给所有参与者一个提交命令,如果有任何一个参与者响应的是准备失败,就发送给所有参与者一个回滚命令。
优点:能利用数据库自身的功能进行本地事务的提交和回滚,不会侵入业务代码,完全由数据库完成。
缺点:
- 阻塞:当所有事物参与者收到命令后,就会执行事务相关操作,但是不会提交事务。在这个期间,如果有更多相同的请求进来,此时因为没有提交事务,那么这些请求都会被阻塞。(增删改都是一种当前读,所以在进行增删改时会先读取数据,读取的是记录的最新版本,同时会对读取的记录进行加锁,这样哪些相同的请求自然无法直径事务就会被阻塞)
- 资源浪费:如果某一个参与者已经挂了,那么第一阶段,其他参与者接收到准备命令,就会执行事务操作,因为一个参与者不可用,那么这些已经执行的实务操作肯定就会回滚,那之前执行的实务操作就是一种资源浪费。
3PC
3PC在2PC的基础上多了一个询问阶段,也就是准备、预提交和提交三个阶段
第一阶段:准备阶段用来判断每个参与者是否正常,防止出现某一个参与者已经挂了,其他的参与者还在执行事务操作。
第二阶段:预提交阶段就是2PC的准备阶段,事务协调者发送一个预提交命令,每一个事物参与者收到命令后,就会执行事务相关操作,但是不提交事务(出来提交事务之外,所有事情都做了),然后参与者返回响应,告诉协调者是否准备成功
第三阶段:提交阶段和2PC的提交阶段一样,如果所有参与者响应的是准备成功,就发送给所有参与者一个提交命令,如果有任何一个参与者响应的是准备失败,就发送给所有参与者一个回滚命令
总结:2PC和3PC都是分布式事务中强一致性的体现,3PC虽然是为了解决2PC的一些问题才出现的,但是多了一个阶段也多了一次通讯的开销,而且参与者挂的概率很低,所以多数都是无用的通讯。所以基于XA协议的2PC和3PC,除非是对强一致性有极高要求的系统,比如金融系统,为了保证资金的安全,会使用这种强一致性的方案。
AT
AT模式是基于最终一致性的技术方案,也是Seata最流行和最常用的事务模式,因为它对业务代码的侵入性很小,并且能够提供良好的性能
AT模式的核心思想是基于UNDO LOG的最终一致性。这个UNDO LOG记录了数据的原始值,也就是数据快照,用于在事务回滚是回复数据,可以分为两个阶段
第一阶段:每个事务参与者执行事务相关操作,并且直接把事务提交,同时把数据的原始值记录在UNDO LOG中,最后返回事务状态
第二阶段:协调者根据所有参与者的执行结果,如果所有参与者结果是成功,就通知参与者异步删除UNDO LOG;如果有参与者的结果是失败的,就通知参与者根据UNDO LOG,恢复成数据的原始值,案后在删除UNDO LOG
AT模式和2PC的区别
AT模式一阶段直接提交事务,2PC不提交事务锁定资源
AT模式依赖数据快照实现数据回滚,2PC依赖数据库机制回滚
AT模式是最终一致性,2PC是强一致性
TCC
TCC分别指的是Try、Confirm、Cancel,也就是业务层面需要写对应的三个方法,所以TCC对代码的侵入很高。分为两个阶段,第一阶段是资源检查预留阶段,第二节u但是提交或回滚。因为他每个阶段都会提交事务,对比2PC阻塞力度变小了(2PC依赖于数据库,而TCC偏向于我们的业务代码实现)
第一阶段Try:尝试执行业务,预留资源,但是不要真正执行操作
第二阶段:如果所有服务上的Try都成功,就Confirm确认执行业务,真正执行操作;有服务Try失败,就Cancel取消业务,释放预留的资源(预留资源就是,比如创建一个订单,状态不要设置为已支付,而是待支付。库存数量可以尝试扣减,但是要设置一个冻结库存的手段,因为这不是真正意义的扣减,如果其他服务失败,这个冻结库存的数量还要还回去。真正执行操作的是第二阶段的confirm,它会把订单状态设置为已支付,而冻结库存的数量清0)
TCC是通过业务代码来实现事务的提交和回滚,对业务的侵入很大,它两阶段提交,它是业务层面的性能比2PC高很多
Saga
Saga事务由一系列本地事务组成,每个本地事务对应于一个服务中的操作,没个服务会按顺序执行事务操作,并通过事件或消息启动下一步。中间有任意一个操作执行失败,就会将之前执行成功的操作反向回滚。当然还为执行的实务操作,根本没有执行自然就不需要回滚
Saga没有像2PC或TCC那样明确定义的阶段,但我们可以从逻辑上将其划分为两个阶段,正常执行阶段和回滚阶段
本地消息
分布式事务的本地消息方案是一种常用的实现最终一致性的方案。他的核心思想是在执行业务操作的同时,将相关的消息存储到本地消息表中,然后通过异步的方式将消息发送到下游服务没从而实现事务的最终一致性
第一阶段:事务参与者A执行事务操作,并提交事务,同时将参与者A要发送给参与者B的消息,插入到本地消息表中,并将消息状态指定为待发送
第二阶段:通过定时任务的方式,定期扫描状态为待发送的消息,然后将消息发送给消息队列,事务参与者B进行消息消费,如果发生异常,可以进行重试,多次重试失败可以添加到死信队列,进行人工处理
事务消息
分布式事务中的事务消息是一种旨在解决分布式事务一致性的消息队列解决方案。利用RocketMQ的事务消息,只能保证本地事务和消息发送的一致性,并不能保证消费者端的一致性,所以也需要借助本地消息表
第一阶段:使用RocketMQ的事务消息机制,事务参与者A保证消息发送和本地事务操作要么一起成功,要么一起失败
第二阶段:与本地消息处理方式一样,事务参与者B进行消息消费,如果发生异常,可以进行重试,多次重试失败可以添加到死信队列,进行人工处理




