背景
随着用户规模和互联网的发展,由原来单一的服务系统转变为多个微服务的分布式系统,一个事务的完成需要多个微服务通过网络共同协同完成。
理论基础CAP
CAP是分布式存储领域中最常用的理论基础。常用的分布式框架 Zookeeper、Etcd、Redis都是以 CAP 理论为基础开发的。
一致性(Consistency): 写操作后的读操作可以获取到最新的数据,数据分布在不同的节点上。一致性具有以下特点。
- 数据同步,写操作有一定延迟。
- 为了保证一致性,会暂时锁定资源。
- 可以返回错误,但是不能返回旧数据。
可用性(Availability): 指所有请求都会在一定时间内得到响应结果,不会出现响应超时或错误。实现可用性需要实现以下特性。
- 数据写入主节点后同步到从节点。
- 保证从节点可用性,同步期间不可对资源锁定。
- 即使同步未完成,也必须理解返回访问结果,所以可能会读到旧数据。
分区容错性(Partition tolerance): 分布式各个节点部署在不同的网络中,其中一个节点崩溃或者网络不可达时,整个系统仍然可以堆外提供服务。分区容错性是分布式系统的基本能力。
在 CAP 理论中,一致性、可用性、分区容错性三者不可同时兼得,一次只能满足其中的两个。
- AP:在数据同步过程中对外的从节点依然可以正常提供服务,无法满足一致性 C。
- CP:数据同步过程中,从节点锁定资源导致服务不可用,无法满足可用性 A。
- CA:系统是单节点部署,不需要考虑数据同步问题。
CAP工程实践
在现实情况下,现实情况下我们面对的是一个不可靠的网络、有一定概率宕机的设备,这两个因素都会导致 Partition,因而分布式系统实现中 P 是一个必须项,而不是可选项。所以对于分布式系统工程实践,就是在一致性C和可用性A之间做出取舍。
但是 C 和 A 又不是非此即彼的关系,在保证可用性的前提下,放宽一致性的约束条件可以使可用性和一致性兼容。CAP 定理证明中的一致性指强一致性,强一致性要求多节点组成的被调要能像单节点一样运作、操作具备原子性,数据在时间、时序上都有要求。如果放宽这些要求,还有其他一致性类型:
- 序列一致性:不要求时序一致,A操作先于B操作,在B操作后如果所有调用端读操作得到A操作的结果,满足序列一致性。
- 最终一致性:放宽对时间要求,保证最终一致性。
BASE理论
BASE 理论是对 AP 理论的扩展,是 Basic Available, Soft state, Eventually consistency 的缩写,意思是基本可用,软状态,最终一致性。出现故障允许部分不可用但保证核心功能可用,允许一段时间的不一致,但最终必须达到一致性。满足 BASE 理论的事务成为 柔性事务。
分布式事务解决方案
两阶段提交协议2PC
2pc是一个非常经典的强一致、中心化的原子提交协议。在两阶段提交中,协调者询问所有参与者是否准备好提交;在第二个阶段,协调者通知它们提交或放弃事务。
阶段1
1)协调者向分布式事务所有参与者发送 canCommit 请求。
2)参与者收到 canCommit 请求后,向协调者发送投票结果(Yes or No),
在投Yes票前,现在持久化介质中保存对象,准备提交。如果投No票,直接返回。
---------------------------------------------------------------------------
阶段2
3)在协调者收到所有参与者的投票
(a)如果不存在故障且投票均为Yes,往所有参与者发送 doCommit。
(b)否则,协调者决定放弃事务,往所有参与者发送 doAbort。
4)所有参与者收到协调者发送的请求doCommit或doAbort请求,并作出提交或者回滚操作
如果是doCommit,还需要向协调者发送haveCommited响应。
2PC实现方案-XA
国际开放组织定义了一套标准的分布式事务模型(DTP模型)
- TM(Transaction Manager):事务管理器,相当于协调者。
- RM(Resource Manager):资源管理器,相当于参与者。
- AP:应用程序。
DTP模型定义TM与RM之间的接口规范成为XA。XA是在传统数据库层面实现的。Oracle和Mysql都支持XA协议。 缺点:
- 需要本地数据库支持XA协议。
- 事务执行过程中资源管理器不释放锁,性能很差。只有完成或者回滚事务锁才会释放。 由于XA性能很差,在实际业务场景中很少会用XA。实现XA协议的框架有 Atomiko, Seata。
补偿方案 TCC
TCC(Try-Confirm-Cancel)又称补偿事务。其核心思想是:"针对每个操作都要注册一个与其对应的确认和补偿"。它分为三个操作:
- Try:预处理,做业务检查和资源预留
- Confirm:确认,做业务确认操作
- Cancel:撤销,实现与Try相反的回滚操作
TCC方案通常有以下步骤:
- TM首先发起所有分支事务Try操作。
- 若Try阶段所有分支执行成功,开始执行Confirm操作。正常情况下TCC默认Confirm不会失败,如果失败需要引入重试或者人工处理。
- 若Try阶段一个或多个分支执行失败,开始执行Cancel操作回滚。
TCC方案通常需要注意一下三种异常:
- 空回滚:在还没有执行Try的情况下调用了二阶段的Cancel。进行Try是分支事务所在服务器网络异常或宕机,恢复时需要进行回滚,但是Try操作并未执行。可以在执行Cancel操作前判断Try是否执行过了。
- 幂等:在Try完成必须确保Confirm和Cancel正确完成,如果发生异常必须重试。重试会导致Confirm和Cancel重复操作,必须保证幂等。
- 悬挂:二阶段Cancel比Try先执行。Try阶段RPC调用超时,Try未达到目标服务,这是TM进行回滚。当Try到达目标服务,Cancel以执行,此时造成Try悬挂。可以通过判断二阶段Cancel是否已经执行,如果执行了一阶段Try不可执行。