目录
1:什么是CAP
2:分布式事务
3:分布式事务解决方案
3.1:2PC的分布式事务解决方案
3.2:3PC的分布式事务解决方案
3.3:TCC(事务补偿)
3.4:MQ事务方案(可靠消息事务)
3.5:Seata分布式事务
在分布式的网络环境中,存在了网络分区,比如大型电商项目,下单模块和减库存模块会分开部署,那么下单后去扣减库存就存在了问题。
先来了解CAP定理:
C:(consistency) 表示一致性,所有节点在同一时间的数据保持完全一致。
A:(Availability)表示可用性,用户访问数据的时候,系统是否能在正常响应时间返回预期的结果。
P: (Partition tolerance) 表示分区容错性,代表分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性或可用性的服务
我们用电商系统的下单和减库存来具体说明CAP原理
在订单和减库存的两个服务中,首先用户下单会创建订单然后调用扣减库存系统,扣减库存。但是两个系统存在不同的服务器上,通过网络通信,就可能存在通信失败的情况,或者减库存系统停机。
这个时候就存在了P(分区容错性)。我们怎么选择CP和AP呢?
CP:保证强一致性的,如果下单成功必须要等待口减库存也成功。在高并发下可能会卡顿,后者库存系统停机,就不能下单了。用户体验较差,保证了强一致性。
AP: 保证了系统了高可用性,比如下单成功后,不等待口减库存成功,异步扣减库存。保证了高可用性。用户体验好,但是可能出现数据不一致问题。
CA: 不存在分区容错性,保证一致性和高可用,那就是传统的大单体架构。在一个数据库中完成下单和减库存。既能保证同一个事务的一致性和可用性。
总结:在分布式架构中我们怎么选择?
比如电商项目的下单减库存:可以选择AP,保证高可用,保住用户体验。牺牲强一致性。
但是在银行项目中和12306抢火车票的项目中,我们经常发现下单之后很卡,等待一小会就会出现票已经卖光了(下单减库存强一致)。这就是CP架构。保证了数据的强一致性。但是牺牲了一些可用性。
总得来说要根据安全和业务来选择AP或者CP。
什么是是分布式事务
我们知道在单机下边的事务通过注解开启事务。但是在分布式的系统中,比如扣减订单和减库存
客户在主业务中,发起了下单和减库存两个服务,这两个服务在不同的服务器上,我们该怎么保证事务的一致性呢?
正常情况下,下单和扣减库存都成功,两边保证了数据的一致性。
但是不正常情况下的,比如下单模块失败或者是超时。导致了库存扣减成功后,下单失败。两边就有了一致性问题,出现了数据不一致。我们无法通过单机的事务来控制。于是就有了分布式事务的问题。
2PC:是两阶段提交协议的缩写
第一阶段:事务协调者询问各个服务是否可以提交事务
1:参与者接收到执行本地的操作,记录undo和redo日志,但是不提交事务
2:如果参与者执行成功后者失败,回消息给事务协调者commit提交或者rollback回滚
第二阶段:事务协调者根据参与者的反馈,如果都能提交,才会发送commit命令,否则你们都他丫的给我回滚。
这个时候各个参与者根据接收到事务协调者的命令来提交或者回滚(根据undo日志回滚),并且执行完毕后发送ack确认给事务协调者,协调者结束本次分布式事务。
2PC的缺点总结:
1:必须要有一个协调者,并且依赖协调者的高可用,协调者不能出问题,否则事务不释放。
2:在第二阶段数据的事务提交的过程中,可能会出现事务协调者告诉参与者你们都可以提交事务的时候,有的参与者没接收到,就没有提交,但是其他的参与者提交的事务,但是数据不一致。
3:大体上保证了在整个分布式事务的过程中数据一致,不保证强一致性
3PC:就是3段式提交的缩写
主要就是优化了2PC的第二阶段的缺点,比如存在事务协调者告知参与者提交事务,但是有的参与者没收到提交命令,导致没有提交事务,但是其他的参与者提交了事务,导致数据不一致。
3PC相比于2PC的主要提升点就是两个改进点
1:添加了超时机制
2:在第一阶段和第二阶段之间添加了一个准备阶段
第一阶段:CanCommit 准备阶段
协调者向多个参与者发送是否可以提交的命令,如果收到了都可以提交,进行到下一个准备提交阶段,否则事务取消
第二阶段:PreCommit 阶段
协调者收到收到可以提交事务的命令,记录undo和redo日志,返回给协调者ack命令,向参与者发送准备提交的命令,参与者收到命令。如果参与者给协调者发送ack命令超时。那么参与者中断事务,任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。
第三阶段:doCommit阶段
进入阶段 3 后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的 do Commit 请求或 abort 请求。此时,参与者都会在等待超时之后,继续执行事务提交。
协调者发送真正的提交命令,各个参与者会提交事务,提交后释放资源。当在参与者收到 preCommit
请求后等待 doCommit
指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
这是第三阶段的中断操作
3PC的优缺点:
相较于2PC,增加的超时机制,超时的情况的会释放事务,取消操作。增加了数据的一致性
数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 doCommit 指令时,此时如果协调者请求中断事务,而协调者因为网络问题无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
神马事TCC?
TCC(Try Confirm Cancel):尝试、确认、取消机制,是一种侵入业务的代码解决方案。是最火的解方案。
也就是说每一个接口都要有三个扩展,try尝试模块,confirm确认模块,cancel取消模块。
TCC分为两个阶段,分别如下:
第一阶段:Try(尝试),主要是对业务系统做检测及资源预留 (加锁,锁住资源)
第二阶段:本阶段根据第一阶段的结果,决定是执行confirm还是cancel
Confirm(确认):执行真正的业务(执行业务,释放锁)
Cancle(取消):是预留资源的取消(出问题,释放锁)
业务案例说明:
考虑最简单的情况:我们有了转机的需求,曲线回家。美团先去川航帮我买票,如果买不到,那么东航也没必要买了。如果川航购买成功,再去东航购买另一张票。
现在问题来了:假设美团先从川航成功买到了票,然后去东航买票的时候,因为天气问题,东航航班被取消了。那么此时,美团必须取消川航的票,因为只有一张票是没用的,不取消就是浪费我的钱。那么如果取消会怎样呢?如果读者有取消机票经历的话,非正常退票,肯定要扣手续费的。在这里,川航本来已经购买成功,现在因为东航的原因要退川航的票,川航应该是要扣代理商的钱的。
那么美团就要保证,如果任一航班购买失败,都不能扣钱,怎么做呢?
两个航空公司都为美团提供以下3个接口:机票预留接口、确认接口、取消接口。美团App分2个阶段进行调用,如下所示:
TCC方案总结:
优点:TCC 事务机制相对于传统事务机制(X/Open XA),TCC 事务机制相比于上面介绍的 XA 事务机制,有以下优点:
性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。
数据最终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。
可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。
缺点:TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。
基于 MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面的协议基本与本地消息表一致。
MQ事务方案整体流程和本地消息表的流程很相似,如下图:
1:业务代码向mq发送half消息,mq把消息持久化到mq的服务器,回复ack
2:业务代码执行本地事务
3:发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。
4:MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。
MQ的优点
消息数据独立存储 ,降低业务系统与消息系统之间的耦合。
吞吐量大于使用本地消息表方案。
缺点
一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) 。
业务处理服务需要实现消息状态回查接口。