分布式事务的概念
要了解分布式事务,我们要先清楚什么是事务。事务就是多个原子操作的组合,他们就像是一条绳上的蚂蚱,要么一起生,要么一起死,在事务中,如果其中一个操作执行失败,那么剩下的操作都不再执行,而之前执行过的操作也需要回滚。至于分布式事务,顾名思义就是包含对分布式系统中不同节点的操作的事务。
事务的目的
我们使用事务的目的是为了防止一些执行失败的操作对数据造成影响,产生错误数据。比较典型的例子就是转账、下订单等关于交易的事务。比如转账业务,我们从A账户转账500块钱给B账户,那么这一条事务的操作就是:从A账户减去500块钱;向B账户中增加500块钱。如果我们执行第一个操作成功,从A账户中扣除了500块钱,但是执行第二个操作失败,B账户中没有增加500块钱。这样子就产生了错误数据,500块钱“凭空消失”了。这时我们就需要使用事务,当第二个操作执行失败后,我们将第一个操作进行回滚,将从A账户中减去的500块钱“还回去”,这样子就保证了数据的正确性。
事务的特性
事务有原子性、一致性、隔离性、持久性四个特性,取英文名的首字母,简称为ACID特性。
1.原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
2.一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
3.隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4.持久性(durability):持久性也称永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
BASE思想
事务的特性是ACID特性,然后我们由CAP定理可知,如果要保证分布式系统的分区容错性,那么一致性和可用性是不可兼得的,但是在分布式系统中缺少任何一个可能都会造成很大影响,为了处理这种情况,就有了BASE思想。
BASE思想是通过牺牲强一致性来满足可用性,然后通过达到最终一致性来保证数据的正确性。BASE思想包含如下三个元素:
1.BA(Basically Available):基本可用,允许损失部分可用性。
2.S(Soft State):软状态,状态可以在一段时间内不同步。
3.E(Eventually Consistent):最终一致,允许在一段时间内数据不一致,数据最终达到一致即可。
软状态是实现BASE思想的方法,基本可用和最终一致是BASE思想的目标。比如在事务的目的中所提的转账的例子,当执行第二个操作时失败,这时我们不回滚第一个操作,然后我们将失败的B操作记录下来,在一定时间内将其执行,这样子就保证了最终一致性。
如何解决分布式一致性问题
保证最终一致性的模式
保证最终一致性的模式是用来解决分布式事务中的某个问题,就像设计模式用来解决系统中的某一个问题。比如事务结束了,但是某个状态不一致;高并发场景下,如何保证基本可用性和最终一致性。
1.查询模式:
任何事务操作都需要提供一个查询接口,用来向外部输出操作执行的状态。事务操作的使用方可以通过查询接口得知事务操作执行的状态,然后根据不同的状态来做不同的处理操作。
2.补偿模式:
通过查询模式,可以知道事务操作的状态,如果操作处于错误的状态,就需要对其未完成的子操作进行修复,来让系统达到一致。这种后期通过努力让系统最终达到一致的操作就叫做补偿。
3.异步确保模式:
异步确保模式是使用补偿模式一个经典例子。在高并发的场景下,把使用方对响应时间要求不高的操作从主流程中摘除,通过异步的方式进行处理,将操作封装后持久化到数据库,然后定时捞取未完成的操作进行补偿模式。
4.定期校对模式:
系统中各节点之间可能存在不一致,就需要定期执行校对操作,如果发现不一致,则运用补偿模式,进行补偿操作,来使系统最终达到一致。
分布式一致性协议
1.两阶段提交协议(2PC):
在分布式事务中,一个事务贯穿多个节点,每个节点仅仅知道自己操作的结果,但是并不知道其它节点的操作结果。为了保证事务的一致性,就需要一个统一的协调者,每个节点把操作的结果告知协调者,协调者再根据操作结果再通知各节点是将操作提交还是取消操作,下图是执行成功的示例:
两阶段提交协议有准备阶段和提交阶段两个阶段,可以保证强一致性,但是复杂,不够灵活,而且还有以下缺点:阻塞(每个节点必须收到响应才会继续向下执行,否则则一直处于阻塞状态,占用资源),节点故障(如果在执行事务的过程中有节点发生故障,则协调者只能等待,会阻塞其他节点资源,而如果发生这种情况后,协调者也发生故障,新选取的协调者无法处理这种情况)。
2.三阶段提交协议(3PC):
三阶段提交协议是两阶段提交协议的改进,目的是解决两阶段协议的缺点。它加入了超时机制来解决两阶段协议的阻塞问题,并在准备阶段前增加了询问阶段(协调者询问节点是否可以提交,节点只需要返回是或否即可)。如果发生响应超时的问题,则可以回滚,不会使节点进入阻塞状态等待。
三阶段提交协议的缺点是需要更多的通讯次数,实现比较复杂。两阶段提交协议和三阶段提交协议都是强一致性的分布式事务解决方案。
分布式事务最终一致性解决方案
1.可靠消息服务:
下图是一个简单的可靠消息服务的设计,通过这张图可以简单的了解一下可靠消息服务的思路。
由上图可以看出,可靠消息服务分为以下三步(括号内为执行失败的情况):
(1)系统A执行操作前先将消息发送给消息系统,系统A在执行完操作后,将确认消息发送给消息系统。(如果系统A执行失败,或者由于网络原因,消息系统一直没有收到确认消息,则将该条消息返回给系统A做处理)
(2)消息系统将消息投递给系统B执行,系统B将执行结果返回给消息系统。(在这里系统B可能执行失败,也可能受网络等因素,消息系统接收到一个失败的结果,遇到这种情况,则重复向系统B投递消息)
(3)如果消息系统收到系统B返回的成功的执行结果,则事务执行成功。(如果消息系统重复投递消息,一直收到失败的结果,超过一定次数后,则认为执行失败,将该条消息存入死信队列,做特殊处理)
可靠消息服务保证消息一直是可靠的,可执行的。所以系统A在执行前先将消息发送给消息系统,执行成功后再发送确认消息,确保系统A成功执行后,消息系统才会将消息发送到B系统,保证消息的可执行性;投递到B系统的消息执行失败后重复投递,超过一定次数后采用补偿模式,做特殊处理,保证消息的可靠性。
2.最大努力通知:
最大努力通知是一种简单的分布式事务最终一致性解决方案,适用于时间敏感度低的业务,且被动方的处理结果不会影响到主动方。
通过下图可以简单的了解一下最大努力通知的执行过程:
由上图可以看出最大努力通知分为以下三步:
(1)主动方执行完业务之后,将消息发送给消息系统。
(2)消息系统将消息通知给被动方,被动方执行成功后响应给消息系统,事务执行成功。(如果消息系统通知消息失败,则重复通知,失败超过一定次数后则不再通知,消息丢失)
(3)被动方根据定时策略主动向主动方发起校对查询,对丢失的消息进行恢复。
最大努力通知的解决方案在事务的执行过程中,消息的传递允许发生丢失,所以消息是不可靠的;被动方根据定时策略,采用补偿模式,主动向主动方发起校对查询(主动方提供校对查询接口),恢复丢失的业务消息。
3.TCC模式:
TCC模式分为Try、Confirm、Cancle三个步骤,它的执行流程简单来说就是:先执行Try操作,如果执行成功,则执行Confirm操作,如果执行过程中出现问题,则执行Cancle操作。TCC模式可以说是一个改进版的两阶段提交协议,但是在执行过程中出现问题时有一定的自我修复能力,如果任何参与者出现了问题,则协调者通过执行Cancel操作来取消之前的操作,达到最终的一致状态。
通过下图可以简单的了解一下TCC模式的执行过程:
上图出自:李艳鹏《分布式服务架构:原理、设计与实战》
(1)应用服务先向各服务提交Try操作,发起预留资源请求,然后执行任务。
(2)如果应用服务执行任务正常,并且各服务都对Try操作返回了OK,管理器则向各服务发起Confirm操作,如果各服务都对Confirm操作返回了OK,事务结束。
(3)如果Try阶段有其中某个服务执行出现了问题,管理器就向各服务发起Cancle操作,来取消操作,释放预留的资源,保证了一致性。如果Confirm阶段有某个服务执行出现了问题,则进行补偿操作。
TCC模式是对两阶段提交协议的一个改进,也是对三阶段提交协议的一个简化。相比较于两阶段提交协议,它首先进行Try操作,询问是否可以预留资源,不会像两阶段提交协议那样长时间占用资源,容易造成阻塞;相比较于三阶段提交协议,它相当于把准备阶段和提交阶段合起来了,相比较于三阶段提交协议通讯次数较少,较为简化。
参考文献
李艳鹏《分布式服务架构:原理、设计与实战》
https://my.oschina.net/xliangbo/blog/1545040