现今互联网界,分布式系统和微服务架构盛行。业界著名的CAP理论也告诉我们,在设计和实现一个分布式系统时,需要将数据一致性、系统可用性和分区容忍性放在一起考虑。
1、CAP理论
在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)3 个要素最多只能同时满足两个,不可兼得。其中,分区容忍性又是不可或缺的。
举例:Cassandra、Dynamo 等,默认优先选择AP,弱化C;HBase、MongoDB 等,默认优先选择CP,弱化A。
2、BASE 理论
核心思想:
数据的一致性模型可以分成以下 3 类:
分布式系统数据的强一致性、弱一致性和最终一致性可以通过Quorum NRW算法分析。
分布式事务的目的是保障分布式存储中数据一致性,而跨库事务会遇到各种不可控制的问题,如个别节点宕机,像单机事务一样的ACID是无法奢望的。
1、Two/Three Phase Commit
2PC,中文叫两阶段提交。在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交。两阶段提交的算法如下:
第一阶段:
第二阶段:
两段提交最大的问题就是第3)项,如果第一阶段完成后,参与者在第二阶没有收到决策,那么数据结点会进入“不知所措”的状态,这个状态会block住整个事务。也就是说,协调者Coordinator对于事务的完成非常重要,Coordinator的可用性是个关键。关注公众号Java面试那些事儿,回复关键字面试,获取最新的面试资料。
因些,我们引入三段提交,三段提交在Wikipedia上的描述如下,他把二段提交的第一个段break成了两段:询问,然后再锁资源。最后真正提交。三段提交的核心理念是:在询问的时候并不锁定资源,除非所有人都同意了,才开始锁资源。但三阶段提交也存在一些缺陷,要彻底从协议层面避免数据不一致,可以采用Paxos或者Raft 算法。
目前两阶段提交、三阶段提交存在如下的局限性,并不适合在微服务架构体系下使用:
2、Try Confirm Cancel(TCC)
一个完整的TCC业务由一个主业务服务和若干个从业务服务组成,主业务服务发起并完成整个业务活动,TCC模式要求从服务提供三个接口:Try、Confirm、Cancel。
整个TCC业务分成两个阶段完成:
第一阶段:主业务服务分别调用所有从业务的try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败,进入第二阶段。
第二阶段:活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功,则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作。
与2PC比较:
缺点:
3、基于消息的分布式事务
核心思想:
eBay 的架构师Dan Pritchett,曾在一篇解释BASE 原理的论文《Base:An Acid Alternative》中提到一个eBay 分布式系统一致性问题的解决方案。它的核心思想是将需要分布式处理的任务通过消息或者日志的方式来异步执行,消息或日志可以存到本地文件、数据库或消息队列,再通过业务规则进行失败重试,它要求各服务的接口是幂等的。
基于消息的分布式事务模式核心思想是通过消息系统来通知其他事务参与方自己事务的执行状态。消息系统的引入更有效的将事务参与方解耦,各个参与方可以异步执行。关注公众号Java面试那些事儿,回复关键字面试,获取最新的面试资料。
该种模式的难点在于解决本地事务执行和消息发送的一致性:两者要同时执行成功或者同时取消执行。
实现上主要有两种方式:
1)基于事务消息的分布式事务
普通消息是无法解决本地事务执行和消息发送的一致性问题的。因为消息发送是一个网络通信的过程,发送消息的过程就有可能出现发送失败、或者超时的情况。超时有可能发送成功了,有可能发送失败了,消息的发送方是无法确定的,所以此时消息发送方无论是提交事务还是回滚事务,都有可能不一致性出现。
解决这个问题,需要引入事务消息,事务消息和普通消息的区别在于事务消息发送成功后,处于 prepared 状态,不能被订阅者消费,等到事务消息的状态更改为可消费状态后,下游订阅者才可以监听到次消息。
本地事务和事务消息的发送的处理流程如下:
2)基于本地消息的分布式事务
基于事务消息的模式对 MQ 系统要求较高,并不是所有 MQ 系统都支持事务消息的,RocketMQ 是目前为数不多的支持事务消息的 MQ 系统。如果所依赖的 MQ 系统不支持事务消息,那么可以采用本地消息的分布式模式。
该种模式的核心思想是:
上游服务:
下游服务:
基于消息的分布式事务可以将分布式系统之间更有效的解耦,各个事务参与方之间的调用不再是同步调用。
对MQ系统的要求较高,对业务实现也有一定的侵入性,要么提供事务消息状态查询接口,要么需要维护本地消息表。并且原则上只接受下游分支事务的成功,不接受事务的回滚,如果失败就要一直重试,适用于对最终一致性敏感度较低的业务场景,例如跨企业的系统间的调用,适用的场景有限。
# 总结
阅读了不少这方面的文章,在此基础上,总结一下分布式事务一致性的解决方案。分布式系统的事务一致性本身就是一个技术难题,目前没有一种很简单很完美的方案能够应对所有场景。分布式系统的一个难点就是因为“网络通信的不可靠”,只能通过“确认机制”、“重试机制”、“补偿机制”等各方面来解决问题。在综合考虑可用性、性能、实现复杂度等各方面的情况上,比较好的选择是“异步消息确保最终一致性”,只是具体实现方式上有一些差异。
作者:阿凡卢 出处:http://www.cnblogs.com/luxiaoxun