分布式事务
一、什么是分布式事务
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
1、分布式事务的基础
1.1 CAP定理
CAP定理,又被叫作布鲁尔定理。对于设计分布式系统(不仅仅是分布式事务)的架构师来说,CAP就是你的入门理论。
C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回50,而不是返回40。
P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
熟悉CAP的人都知道,三者不能共有,如果感兴趣可以搜索CAP的证明,在分布式系统中,网络无法100%可靠,分区其实是一个必然现象,如果我们选择了CA而放弃了P,那么当发生分区现象时,为了保证一致性,这个时候必须拒绝请求,但是A又不允许,所以分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。
对于CP来说,放弃可用性,追求一致性和分区容错性,我们的zookeeper其实就是追求的强一致。
对于AP来说,放弃一致性(这里说的一致性是强一致性),追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的BASE也是根据AP来扩展。
顺便一提,CAP理论中是忽略网络延迟,也就是当事务提交时,从节点A复制到节点B,但是在现实中这个是明显不可能的,所以总会有一定的时间是不一致。同时CAP中选择两个,比如你选择了CP,并不是叫你放弃A。因为P出现的概率实在是太小了,大部分的时间你仍然需要保证CA。就算分区出现了你也要为后来的A做准备,比如通过一些日志的手段,是其他机器回复至可用。
1.2 理解BASE理论
eBay的架构师Dan Pritchett源于对大规模分布式系统的实践总结,在ACM上发表文章提出BASE理论,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。
BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。
A、基本可用(Basically Available)
基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
如:电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
B、软状态( Soft State)
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。
C、最终一致性( Eventual Consistency)
最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。
D、ACID和BASE的区别与联系
ACID是传统数据库常用的设计理念,追求强一致性模型。BASE支持的是大型分布式系统,提出通过牺牲强一致性获得高可用性。
ACID和BASE代表了两种截然相反的设计哲学,在分布式系统设计的场景中,系统组件对一致性要求是不同的,因此ACID和BASE又会结合使用。
2、一致性模型
数据的一致性模型可以分成以下 三 类:
- 强一致性:数据更新成功后,任意时刻所有副本中的数据都是一致的,一般采用同步的方式实现。
- 弱一致性:数据更新成功后,系统不承诺立即可以读到最新写入的值,也不承诺具体多久之后可以读到。
- 最终一致性:弱一致性的一种形式,数据更新成功后,系统不承诺立即可以返回最新写入的值,但是保证最终会返回上一次更新操作的值。
分布式系统数据的强一致性、弱一致性和最终一致性可以通过Quorum NRW算法分析。
3、分布式事务解决方案
3.1 两段提交(2PC)
在XA协议中分为两阶段:第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。
成功的处理情况:
失败处理情况:
两段提交方案的优缺点:
优点:尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现。
缺点:
- 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
- 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
3.2 补偿型(TCC)
TCC事务其实主要包含两个阶段:Try阶段、Confirm/Cancel阶段。从TCC的逻辑模型上我们可以看到,TCC的核心思想是,try阶段检查并预留资源,确保在confirm阶段有资源可用,这样可以最大程度的确保confirm阶段能够执行成功。
3.2.1、try-尝试执行业务
- 完成所有业务检查(一致性)
- 预留必须业务资源(准隔离性)
3.2.2、confirm-确认执行业务
- 真正执行业务
- 不作任何业务检查
- 只使用Try阶段预留的业务资源
- Confirm操作必须保证幂等性
3.2.3、cancel-取消执行业务
- 释放Try阶段预留的业务资源
- Cancel操作必须保证幂等性
3.3 RockectMQ消息事务
当消息生产者本地事务处理成功与消息发送成功不一致时,传统的处理方式无法解决该问题,事务消息实现了消息生产者本地事务与消息发送的原子性,保证了消息生产者本地事务处理成功与消息发送成功的最终一致。用户实现类似 X/Open XA 的分布事务功能,通过 CMQ 事务消息能达到分布式事务的最终一致。
其中,事务消息发送对应步骤1、2、3、4,事务消息回查对应步骤5、6、7。
- 生产者向 MQ 服务端发送消息(为了方便说明,整个 MQ 服务端用 MQ server 来表示)。
- MQ Server 将消息持久化成功之后,向生产者 ACK 确认消息已经发送成功,此时消息为半消息(暂不能投递的消息)。
- 生产者发送消息成功后,开始执行本地事务逻辑。
- 生产者根据本地事务执行结果向 MQ Server 提交二次确认(Commit 或是 Rollback),MQ Server 收到 Commit 状态则将半消息标记为可投递,消费者最终将收到该消息;MQ Server 收到 Rollback 状态则删除半消息(直接置消息已消费状态),消费者将不会接受该消息。
- 在断网或者是生产者应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。
- 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。生产者根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤4对半消息进行操作。
3.3.1、发送事务消息步骤
发送事务消息包含以下两个步骤:
- 发送半消息并执行本地事务
- 提交本地事务执行状态
其中,提交本地事务执行状态有两种方式:
- 执行本地事务完成后,SDK 主动提交。
- 执行本地事务后一直没有提交状态,MQ Server 会主动发送回查,此时 SDK 提交本地事务执行状态。
事务执行状态有以下三种情况:
- TransactionStatus.COMMIT 提交事务,消费者可以消费到该消息。
- TransactionStatus.ROLLBACK 回滚事务,消息被丢弃,消费者不会消费到该消息。
- TransactionStatus.UN_KNOW 无法判断状态,等待 MQ Server 再次发送回查。
来源: