CAP理论&常用的分布式事务概览

CAP理论&常用的分布式事务概览

CAP

CAP理论是分布式系统的重要理论,即一个分布式系统最多只能同时满足一致性(C: Consistency)、可用性(A: Availability)、分区容错性(Partition tolerance)这三项中的两项。

一致性

数据能一起变化,能让数据整齐划一。

可用性

每一个非故障节点接收的任何请求,都能处理并返回响应结果。

分区容错性

分区:分布式系统中,节点之间通信出现了问题。

如果出现了分区问题,系统仍然可以运行。

权衡

在分布式系统中,网络异常不可避免,所以P往往无法忽略,所以需要考虑在发生分区故障时,如何选择CA

CP

系统一旦发生分区故障后,允许系统停机或者长时间无响应,但系统每个节点总是会返回一致的数据。如:分布式协调系统Zookeeper、分布式存储系统Redis等,数据一致性是最基本的要求。

AP

如果系统发生分区故障后,依然可以访问系统,但是无法保证全局数据的一致性(舍弃数据的强一致性,退而求其次保证最终一致性)。如:Eureka

CAP如何权衡和取舍没有好坏之分,需要根据不同的业务场景进行选择,适合的才是最好的。对于涉及到钱这种不能有任何差错的场景,数据强一致性是必须要保证的。对于其他场景,比较普遍的做法是选择可用性和分区容错性,舍弃强一致性,退而求其次使用最终一致性来保证数据的安全

BASE

既然无法保证强一致性(Strong Consistency),应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

BASE指基本可用(Basically Available)、软状态(Soft State)、最终一致性(Eventual Consistency)。

基本可用

分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。

  • 电商大促,流量激增,部分用户可能会被引导到降级页面,服务层也可能提供降级服务;
  • 网络异常,接口响应从0.5s增加到2s。

软状态

允许系统存在中间状态,而该中间状态不会影响系统整体可用性。即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。如MySQL主从节点间的数据同步。

最终一致性

统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

常见分布式事务

分布式事务可以简单的理解为协调多个资源,达到共同提交或共同失败的效果,及分布式ACID。

两阶段提交(2PC)

分布式系统中,所有的节点虽然都可以知道自己执行后的状态,但无法知道其它节点执行后的状态,一个事务跨越多个系统时,需要引入一个协调者的组件来统一各个节点的执行结果是否提交。

  1. 准备阶段

    协调者询问事务的参与者是否可以执行操作,并等待参与者响应,参与者会执行相应的事务操作并记录重做和回滚日志,所有执行成功的参与者向协调整发送AGREEMENT 或者 ABORT 表示执行操作的结果。

  2. 提交阶段

    当事务的所有参与者都决定提交事务时,协调者会向参与者发送 COMMIT 请求,参与者在完成操作并释放资源之后向协调者返回完成消息,协调者在收到所有参与者的完成消息时会结束整个事务;与之相反,当有参与者决定 ABORT 当前事务时,协调者会向事务的参与者发送回滚请求,参与者会根据之前执行操作时的回滚日志对操作进行回滚并向协调者发送完成的消息,在提交阶段,无论当前事务被提交还是回滚,所有的资源都会被释放并且事务也一定会结束。

问题

  1. 阻塞;
  2. 两阶段提交的执行过程中,如果协调者永久宕机,部分参与者将永远无法完成事务;整个事务不一致。

三阶段提交(3PC)

三阶段提交相较于二阶段提交引入了超时机制询问阶段,如果协调者或者参与者在规定之间内没有接受到来自其它节点的响应,就会根据当前的状态选择提交或终止整个事务。

3PC 2PC

询问 准备

准备

提交 提交

3PC的询问阶段,对应的才是2PC的准备阶段,都是ASK参与者是否准备好了,但2PC的执行过程是阻塞的,一个资源在进入准备阶段之后,必须等所有的资源准备完毕后才能进入下一步,对全局一无所知。而3PC拆分出询问阶段,在确保所有参与者建康良好的情况下,才会发起真正的事务处理,在效率和容错性上更胜一筹。而且3PC在准备阶段,如果超时,就认为失败;在提交阶段如果超时还会继续执行下去。整个事务不会一直等待下去。

问题

准备与提交阶段不是原子的,与2PC一样,依然存在一致性问题。

TCC

补偿事务,为每一个操作,都准备一个确认动作和相应的补偿动作。

  1. try 尝试阶段

    尝试锁定资源;

  2. confirm 确认阶段

    尝试将锁定的资源提交;

  3. cancel 取消阶段

    其中某个环节执行失败,发起事务取消动作。

如:资金转账:try就是冻结金额;confirm就是完成扣减;cancel就是解冻,只要对应的订单号是一直的,多次执行也不会有任何问题。

问题

需要编码实现,额外精力设计TCC逻辑。

框架

tcc-transaction、seata等。

SAGA

很多业务场景下,只需要保证业务的最终一致性。SAGA通过消息来协调一系列的本地子事务,来保证最终一致性。

与TCC相比,SAGA少了try,需要提供执行逻辑和补偿逻辑。在每一个本地事务中都会向集群中的其他服务发送一条的新的消息来触发下一个本地的事务;一旦本地的事务因为违反了业务逻辑而失败,那么就会立刻触发补偿逻辑来撤回之前本地事务造成的副作用。

问题

  1. 嵌套问题,SAGA只允许两层嵌套,层次过深的话,消息流转会太复杂;
  2. 补偿操作无法保证执行成功,可以记录日志或人工参与;
  3. 本地小事务不是同时提交的,执行过程中,会产生脏数据。

框架

seata

本地消息表

解决数据库事务和MQ之间的事务问题。

有一个分布式事务,在正常落库之后,需要通过MQ来协调后续业务的执行。

  1. 正常写入数据库;写入本地消息表(记录MQ消息处理状态,发送中已完成),此处使用本地事务
  2. 写入本地消息表成功后,异步发送MQ消息;
  3. 后续业务订阅MQ消息,消费成功后,把执行成功的状态再通过MQ来发送。本地业务订阅这个执行状态,并把消息表中对应的记录状态,改为已完成;如果消费失败,则不做处理;
  4. 存在一个定时任务,持续扫描本地消息表中,状态为发送中的消息(注意延时),并再次把这些消息发送到MQ,重复2的过程。

问题

  1. 需要额外编码,与业务耦合;
  2. 本地消息表需要写数据库,增加数据库压力。

总结

实际中,普遍选用软事务,TCC、SAGA、本地消息表。SAGA应对长事务特别拿手,但隔离性稍差;TCC需要较多编码,适合少量的分布式事务流程;本地消息表应用场景有限,耦合业务不能复用。各有利弊,需要结合使用场景进行选择。

请作者喝瓶肥宅快乐水