(前提是对事务特性了解)
从隔离性的使用场景能感受到,提出此特性也是有时代背景的,也是人们为了解决并发控制问题而提出的对策。
1、何为「分布式事务」
在事务的基础上,加上了「分布式」,这意味着要在分布式的环境下满足事务的ACID特性。而分布式意味着网络可能是异构的,操作系统也不尽相同,数据库系统也不只在一台主机上了。
实际上,随着数据规模的急剧增长,单体数据库无法承载如此海量的数据,需要对原有数据进行合理的分库分表,此外,对于近来盛行的「微服务」架构,每个服务独立部署,有独立的数据库,也不得不面临分布式事务的问题。
总之,随着分布式架构的大规模运用,分布式事务是不可回避的问题。
如果我们将「分布式事务」认为是一种事务,那么又该如何设计,使其满足ACID四大特性呢?
2、两段式提交
最经典的分布式事务解决方法就是“两段式提交(two-phase commit)”。
在两段式提交过程中,涉及两类角色, 协调者(Coordinator) 和 参与者(Participants) 。
顾名思义,“两段式提交”将事务的提交过程分为了两个阶段:
第一个阶段:预提交阶段,也可以称之为投票阶段。在这个阶段,协调者询问所有的参与者是否已准备好提交事务,参与者通常都会给出一个“YES or NO”的回答,即我们认为的投票过程。根据事务的Atomic特性,但凡有一个参与者Say NO,那么协调者就认为这个事务提交是失败的。只有全部参与者给出“YES”的回答,才能让事务顺利提交。
第二个阶段:提交决定阶段。协调者根据上一个阶段的投票结果决定是Commit还是Abort,这个决定是全局性的,会通知到所有的参与者执行最终的决定,并回传一个ack确认信息。
值得注意的是,在进入两段式提交过程期间,所有的参与者不能临时改变主意,即投票不可反悔,下面是两段式提交过程的示意图:
two-phase commit protocol
仔细想想,两段提交协议也并不完美。
两段式提交时一个典型的 中心化架构 协议,被指定为协调者的节点如果没有做高可用措施的话,协调者的宕机意味着事务再也无法正常提交了。与此同时,各个节点(包括协调者和参与者)只有在永无故障的前提下(虽然绝大多数的时候是OK的),才能使得事务顺利提交。
这些假设条件无疑是严苛的,因为要达到 强一致性 的目的。
不过,这也使得两段式提交协议在某些时候是比较脆弱的。两段式提交协议的性能比较差, 消息交互多,且受最慢节点影响。
基于两段式提交的分布式事务在提交事务时需要在多个节点之间进行协调,最大限度地推后了提交事务的时间点。
客观上延长了事务的执行时间,同时也会
提高事务在访问共享资源时发生冲突和死锁的几率
。随着数据库节点的增多,这种趋势会越来越严重,从而成为系统在数据库层面上水平伸缩的"枷锁"。 这是很多Sharding系统不采用分布式事务的主要原因。
就好比你在旅游网站上订了一个行程,除了生成一个出行订单外,还需要去航空公司为你预订相应的机票,还要去酒店为你预订每天的房间。如果根据两段式提交协议,只有当订单、机票和房间都准备好了,这个行程才算预订好了,这在显然是不合理的。对于这种长生命周期的分布式事务就不适合两段式提交。
3、三段式提交
显然,三段式提交协议是基于两段式提交而生的,为了解决两段式提交带来的阻塞等待问题,三段式提交引入TIMEOUT机制,可在超时后自动释放资源。
和两段式提交一样,三段式提交协议有两类角色, 协调者(Coordinator) 和 参与者(Participants) ,由三个阶段构成。
第一个阶段:询问阶段。协调者询问每个参与者是否可以进行提交,这时候会出现多种情况。参与者明确自己是否能提交,可以给出“YES or NO”的准确回答,也有可能因为各种因素,导致不能确定,直到此次询问超时,返回“NO”。
第二个阶段:预提交阶段。根据上阶段得到的应答,协调者决定事务Commit or Abort,将投票最终结果发送给各个参与者,参与者收到此决定后再继续下面的操作,只不过到了此阶段,双方都有超时机制了。协调者也有可能因为各种原因不能及时做出决定,超时后就自动给出了Abort决定,与此同时,参与者收到了协调者的决定,需要回传ACK信息以确定,如果没有在规定的时间窗口内确认,协调者认为事务应该Abort。
第三个阶段:正式提交阶段。在上一个阶段,各个参与者已经收到了事务Commit or Abort的确认信息,其实这个阶段可以认为是一个二次确认阶段,协调者会发送一个DoCommit指令,参与者才真正开始进行事务的操作,并给协调者回复一个ACK。如果此时协调者接收ACK超时,协调者也会Abort整个事务。值得注意的是,如果协调者本身发送DoCommit就超时了,参与者也不会直接Abort事务,而是按照第二个阶段的结果执行。
下面附上两段式提交与三段式提交的框架图:
4、TCC
TCC包含了三个阶段: T ry, C onfirm, C ancel,因此而得名「TCC」。
TCC的概念属于国产,因为支付宝的技术布道而广为人知。
其实,TCC算是一种编程模型,通常被理解为是一种柔性事务解决方案。
Try ,尝试执行业务。完成所有业务检查,预留相应的业务资源。
Confirm ,如果Try阶段执行成功,则此阶段利用Try阶段预留的资源,不再进行业务检查,而是执行真正的业务提交。并且Confirm阶段的操作满足幂等性,以便支持重试。这个阶段有一个假设:只要Try阶段成功,那么Confirm阶段一定成功。
Cancel ,此阶段是发生在Try阶段出现失败的时候,回滚之前的操作,释放Try阶段预留的业务资源,同样也满足幂等性。
借用一个例子:
用户下完订单后,使用红包帐户和资金帐户来付款,红包帐户服务和资金帐户服务在不同的系统中。一个是CapitalTradeOrderService,代表着资金帐户服务,另一个是RedPacketTradeOrderService,代表着红包帐户服务。
下完订单后,订单状态为DRAFT,在TRY阶段,订单支付服务将订单状态变成PAYING,同时远程调用红包帐户服务和资金帐户服务,将付款方的余额减掉(预留业务资源);
如果在Try阶段,任何一个服务失败,将会调用这些服务对应的cancel方法,订单支付服务将订单状态变成PAY_FAILED,同时远程调用红包帐户服务和资金帐户服务,将付款方余额减掉的部分增加回去;
如果Try阶段正常完成,则进入Confirm阶段,在Confirm阶段,订单支付服务将订单状态变成CONFIRMED,同时远程调用红包帐户服务和资金帐户服务对应的Confirm方法,将收款方的余额增加。
总结
本文对事务进行了简单介绍,重点分析了事务的ACID四大特性,接着介绍分布式事务的解决方案。
希望对你有所帮助!
来自:http://mp.weixin.qq.com/s/UKNK9UzdiZzrNCd5U_4Ytg