前言
传统的串行式系统设计或者使用mvc风格的系统设计保证数据一致性都是使用关系型数据库,好处是使用ACID事务特性,保证数据一致性只需要开启一个事务,然后执行更新操作,最后提交事务或回滚事务。更方便的是可以以借助于Spring等数据访问技术和框架后只需要关注引起数据改变的业务本身即可。
但是目前,随着QPS的不断增加,业务量的不断加大,all in one架构已经完全不能满足这庞大的数据量和业务量,就需要对业务进行水平拆分,拆分后就会出现两个或两个以上的数据库的情况,此时不能依靠本地事务得以解决,需要借助于分布式事务来保证一致性。
在讲实际前面不防讲点理论,比如传统的关系型数据库有ACID,所以分布式事务也是有一定特性的。
CAP理论
CAP理论算是验证一个分布式事务是否可靠的一个标准。
一致性Consistency
指分布式系统中的所有数据备份在同一时刻是否拥有同样的值。
可用性Availability
指在集群中一部分节点故障后,集群整体是否还能正常响应请求。
分区容错性PartitionTolerance
指你这个分布式系统如果不能非常及时保证数据一致性,那这个容错的时间间隔是多少。
相当于通信的时限要求,系统如果不能在一定时限内达到数据一致性,那系统应该怎么做。
由于当前网络硬件肯定会出现延迟丢包等通信异常问题,所以分区容错性必须实现。
BASE思想
BASE理论是对CAP理论的延伸,基本思想是即使无法做到强一致性,应用可以采用适合的方式达到最终一致性。
也可以简单理解Wie一个分布式系统的底线。
基本可用BasicallyAvailiable
指分布式系统在出现故障的时候,可以损失部分可用性,需要保证和核心可用。服务限流和降级就是其基本表现。
软状态SoftState
指允许系统存在中间状态,而中间状态不会影响整体可用状态。分布式存储中一般一份数据都有有若干个副本,允许不同节点间副本同步的延时就是软状态的体现。
最终一致性EventualConsistency
指系统中所有的数据副本经过一定时间后,最终能够达到一致的状态。
CAP的一致性就是强一致性,这种一致性级别是最符合用户直觉的,用户体验好,但实现起来对系统性能影响较大弱一致性正好相反。BASE中的最终一致性可以看做是弱一致性的一种特殊情况。
对比总结
CAP理论和BASE思想讨论的意义在于即使无法做到强一致性,我们也可以采用合适的方式达到最终一致性。
对于微服务架构,建议采用一种更为松散的方式来维护一致性,也就是所谓的最终一致性,对于开发者而言,实现最终一致性的方案可以根据不同的业务场景做不同的选择。
强一致性解决方案
2PC
2PC分布式事务主要分为俩个阶段。
- 第一阶段:
准备阶段:由协调者提议并收集其他参与者的反馈(提议的节点为协调者,参与决议的节点为参与者)。然后将事务逻辑如果都ok的话直接将事务逻辑发放给给个参与者。 - 第二阶段:
执行阶段:根据反馈决定提交或中止事务。每个节点依据协调者发来的事务逻辑自己执行并且自己记录redolog和undolog,如果执行完都没有发生错误就给协调者返回正常,如果都返回正常协调者就会让参与者提交事务,只要有一个节点给协调者返回错误,协调者就会通知其他节点回滚数据。
总之就是,协调者发起一个提议分别询问各参与者是否接受场景,然后协调者根据参与者的反馈,提交或中止事务。
2PC的延迟问题。
问题一:同步阻塞问题
执行过程中,所有参与者都是事务阻塞型的。当参与者占用公共资源时,其他第三方节点访问公共资源就不得不处于阻塞状态。
问题二:单点故障
一旦协调者发生故障,参与者会一直阻塞下去。特别是在第二阶段,协调者发生故障,则所有参与者还处于锁定资源的状态中,但是无法完成后续的事务操作。
问题三:数据不一致
当协调者向参与者发送提交请求后发生了局部网络异常,或者在发送提交请求过程中协调者发生了故障,就会导致只有一部分参与者接收到了提交请求,这部分参与者接到请求后就会执行提交操作,而未接收到提交请求的机器就无法执行事务提交,于是就出现了数据不一致的问题。
3PC
与2PC相比,3PC主要有两个改动点:在协调者和参与者之间都引入了超时机制+把准备阶段一分为二。
3PC:CanCommit + PreCommit + DoCommit,具体操作如下。
CanCommit阶段:协调者向参与者发送提交请求,参与者如果可以事务操作就返回Yes响应,否则返回No响应。此处如果有参与者不能进行事务操作,协调者就直接终止事务,降低了参与者阻塞的时间。
PreCommit阶段:协调者根据参与者的响应情况来决定是否可以进行事务的PreCommit操作。
如果协调者从所有参与者获得反馈都是Yes响应,那么就派发任务让参与者执行事务的逻辑。
如果有任何一个参与者向在接受任务期间向协调发送了No响应,或者协调者派发任务后等待超时没有收到参与者的ACK响应,那么就通知其他参与者执行事务的中断。
DoCommit阶段:执行提交或中断事务,并且参与者自己记录事务的redolog和undolog。当协调者收到异常或者等待超时没有收到Commited,就通知其他参与者回滚。
可见,3PC主要解决了2PC的单点问题和同步阻塞问题。就是使用多一次询问,最后执行来见降低阻塞时间,提高事务执行的成功率,降低回滚率。
最终一致性解决方案
补偿模式
基本思路在于使用一个额外的补偿服务来协调各个需要保证一致性的微服务,补偿服务按顺序依次调用各个微服务,如果某个微服务调用失败就撤销之前所有已经完成的微服务,补偿服务对需要保证一致性的微服务提供补偿操作。
举例当中涉及两个微服务,订单微服务和支付微服务,为其提供补偿操作,如果支付服务失败,就需要取消之前的下单服务。
关键点
对于补偿服务而言,所有服务的操作记录是一个关键点,操作记录是执行取消操作的前提。
举例中,订单服务与支付服务需要保存详细的操作记录和日志,这些日志和记录有助于确定失败的步骤和状态,进而明确需要补偿的范围,然后获取所需补偿的业务数据。
如果只是订单服务失败,那么只需要补偿一个服务就可以,如果支付服务也失败了,对两个服务进行回滚。
补偿操作要求业务数据包括支付时的业务流水号、账号和金额。理论上可以根据唯一的业务流水号就能完成补偿操作,但提供更多的数据有益于微服务健壮性。
解决方案
实现补偿模式的关键要素就是记录完整的业务流水,可以通过业务流水为补偿操作提供需要的业务数据。
补偿服务可以从业务流水的状态中知道补偿的范围,补偿过程中需要的业务数据同样可以从记录的业务流水中获取。
补偿服务作为一个服务调用过程同样存在调用不成功的情况,需要通过一定的健壮性机制来保证补偿的成功率,补偿的相关操作本身需要具有幂等性。
(1)服务重启:如果失败的原因不是暂时的,而是由业务因素导致的业务错误,需要对问题进行修正后重新执行。
(2)立即重试:对于网络失败或数据库锁等瞬时异常,重试在很大程度上能够确保任务正常执行。
(3)定时调用:一般会指定调用的次数上限,如果调用次数达到上限也就不再进行重试。
如果通过服务重启、立即重试、定时调用等策略依旧不能解决问题,则需要通知相关人员进行处理,即人工干预模式。
TCC模式
一个完整的TCC业务由一个主服务和若干个从服务组成,主服务发起并完成整个业务流程。
从服务提供三个接口:Try、Confirm、Cancel:
Try接口:完成所有业务规则检查,预留业务资源。
Confirm接口:真正执行业务,其自身不做任何业务检查,只使用Try阶段预留的业务资源,同时该操作需要满足幂等性。
Cancel接口:释放Try阶段预留的业务资源,同样也需要满足幂等性。
举例来看,订单系统拆分成订单下单和订单支付两个场景
1)Try阶段:尝试执行业务。
一方面完成所有业务检查,如针对该次订单下单操作,需要验证商品的可用性以及用户账户金额是否够。
另一方面需要预留业务资源,如把用户账户余额进行冻结用于支付该订单,确保不会出现其他并发进程扣减账户余额导致后续支付无法进行。
(2)Confirm阶段:执行业务。
Try阶段一切正常,则执行下单操作并扣除用户账号中的支付金额。
(3)Cancel阶段:取消执行业务。
释放Try阶段预留的业务资源,如果Try阶段部分成功,如商品可用且正常下单,但账户余额不够而冻结失败,则需要对产品下单做取消操作,释放被占用的该商品。
TCC总结
TCC服务框架不需要记录详细的业务流水,完成Confirm和Cancel操作的业务由业务服务提供。
在实现TCC模式上,最重要的工作是设计一个稳定的、高可用的、扩展性强的TCC事务管理器。
在一个跨服务的业务操作中,首先通过Try锁住服务中业务资源进行资源预留,只有资源预留成功了,后续的操作才能正常进行。Confirm操作是在Try之后进行的对Try阶段锁定的资源进行业务操作,Cancel在所有操作失败时用于回滚。
TCC的操作都需要业务方提供对应的功能,在开发成本上比较高,推介TCC框架有:
http://github.com/protera/spring-cloud-rest-tcc;
文章来源: blog.csdn.net,作者:Listen-Y,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/Shangxingya/article/details/115041755