根据微服务架构鼻祖MartinFowler的建议,在微服务架构中应该尽量避免分布式事务。然而,在某些领域,分布式事务作为敌对的对手是不可避免的。在工程领域,分布式事务的讨论主要集中在强一致性和最终一致性的解决方案上。典型的方案包括:两阶段提交(2PC,Two-phaseCommit)方案。eBay事件队列方案。TCC补偿模式。缓存数据的最终一致性。一致性理论分布式事务的目的是为了保证分库中数据的一致性,跨库事务会遇到各种不可控的问题,比如个别节点崩溃,无法预料单机事务之类的ACID。另外,业界著名的CAP理论也告诉我们,对于分布式系统来说,数据一致性、系统可用性、分区容忍度需要综合考虑,权衡取舍。Two-PhaseCommitProtocol(简称2PC)是比较经典的分布式事务实现方案,但是2PC的扩展性很差,在分布式架构中的应用成本比较高。eBay架构师DanPritchett提出了BASE理论来解决大规模分布式系统中的数据一致性问题。BASE理论告诉我们,可以通过放弃系统每时每刻的强一致性来换取系统的可扩展性。01.CAP理论在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(PartitionTolerance)这三个要素最多只能同时满足两个,不能同时拥有。其中,partitiontolerance是必不可少的。一致性:在分布式环境中,多个节点的数据是否强一致。可用性:分布式服务始终可以保证可用。当用户发送请求时,服务可以在限定时间内返回结果。分区容忍度:特指对网络分区的容忍度。例如:Cassandra、Dynamo等,默认首选AP,弱化C;HBase、MongoDB等,默认首选CP,弱化A。02.BASE理论的核心思想:基本可用(BasicallyAvailable):是指当一个分布式系统发生故障时,允许损失部分可用性,以保证核心可用。软状态(SoftState):指允许分布式系统存在的中间状态,中间状态不会影响系统的整体可用性。最终一致性:是指分布式系统中的所有副本数据经过一定时间后最终能够达到一致状态。一致性模型数据的一致性模型可以分为以下三类:强一致性:数据更新成功后,任何时候所有副本中的数据都是一致的,一般采用同步方式实现。弱一致性:数据更新成功后,系统不承诺立即读取***写入的值,也不承诺需要多长时间才能读取到。最终一致性:弱一致性的一种形式。数据更新成功后,系统不承诺立即返回***写入的值,但保证最终会返回上次更新操作的值。分布式系统数据的强一致性、弱一致性和最终一致性可以通过QuorumNRW算法进行分析。分布式事务解决方案01.2PC方案-强一致性2PC的核心原理是通过分阶段提交和日志记录事务提交的阶段状态。关闭后组件重启后,事务可以通过日志恢复到该阶段的状态,在该状态节点提交并重试。比如Coordinator重启后,可以通过日志判断提交是处于Prepare还是PrepareAll状态。如果是前者,说明可能有部分节点Prepare不成功,或者所有节点都Prepare成功但还没有发出Commit,状态恢复后会向所有节点发出RollBack。如果是PrepareAll状态,Commit需要下发到所有节点,数据库节点需要保证Commit是幂等的。2PC方案的三个问题:同步阻塞。数据不一致。单点问题。升级后的3PC方案就是为了解决这些问题,主要有两个改进:增加超时机制。在两个阶段之间插入一个准备阶段。但是,三阶段提交也存在一些缺陷。为了完全避免协议层面的数据不一致,可以使用Paxos或Raft算法。02.eBayEventQueueSolution-UltimateConsistencyeBay架构师DanPritchett曾在一篇解释BASE原理的论文《Base:An Acid Alternative》中提到一个解决eBay分布式系统一致性问题的方案。它的核心思想是通过消息或日志异步执行需要分布式处理的任务。消息或日志可以存储在本地文件、数据库或消息队列中,然后通过业务规则进行失败重试。它要求每个服务接口都是幂等的。描述的场景是有一个用户表user和一个transaction表transaction,user表存储用户信息,销售总额和采购总额。交易表存储每笔交易的序号、买家信息、卖家信息和交易金额。如果产生交易,需要在交易表中增加一条记录,同时修改用户表中的金额。论文中提出的解决方案是在本地事务中更新事务表记录和用户表更新消息。为了避免重复消费用户表更新消息带来的问题,增加了一个操作记录表updates_applied来记录完成的事务。相关信息。该方案的核心在于第二阶段的重试和幂等执行。失败后重试是一种补偿机制,是保证系统最终一致性的关键过程。03.TCC(Try-Confirm-Cancel)补偿模式-最终一致性某业务模型如图所示,是由ServiceA、ServiceB、ServiceC、ServiceD组成的微服务架构体系,ServiceA需要:依次调用服务B、服务C、服务D完成一个操作。当服务A调用服务D失败时,为保证整个系统数据的一致性,需要回滚服务B和服务C的invoke操作,进行反向回滚操作。回滚成功后,整个微服务系统在数据上是一致的。实施的三个关键要素:必须记录服务调用链。每个服务提供者需要提供一组与业务逻辑相反的操作来相互补偿,回滚操作必须是幂等的。必须根据失败原因实施不同的回滚策略。实现难点二:补偿模式的特点是实现简单,但在一定程度上很难形成通用的解决方案,尤其是服务链的记录,因为很多时候业务参数或者业务逻辑千差万别。许多业务特性使该服务无法提供安全的回滚操作。04.缓存数据最终一致性在我们的业务系统中,缓存(Redis或者Memcached)通常是在数据库的前面作为数据读取的缓冲区,这样I/O操作就不会直接落在数据库上。以商品详情页为例,如果卖家修改商品信息并写回数据库,但此时用户从商品详情页看到的信息仍然是从缓存中获取的过期数据,这会造成缓存系统和数据库系统中的数据不一致。针对该场景下缓存与数据库数据不一致的问题,我们有以下两种解决方案:为缓存数据设置一个过期时间。当缓存中的数据过期时,业务系统会从数据库中取出数据,并将新值放入缓存中。这个过期时间就是系统达到最终一致性的容忍时间。更新数据库数据后,同时清除缓存数据。数据库数据更新后,同步删除缓存中的数据,以便下次直接从数据库中获取商品详情并同步到缓存中。选型建议在面对数据一致性问题时,我们首先要从业务需求的角度来确定自己对三种一致性模型的接受程度,然后通过具体的场景来确定解决方案。从应用的角度来看,分布式事务的现实场景往往是不可避免的,在能够提供其他解决方案之前,2PC也是一个不错的选择。对于电商、购物转账等金融业务,2PC在中间件层最大的问题是业务不可见,一旦出现不可抗力或意外的一致性违规。如果数据节点突然宕机,业务很难根据2PC日志进行补偿。在金融场景中,数据一致性是生命线,业务需要对数据有绝对的控制权。推荐使用TCC等分布式事务模型,或者基于消息队列的灵活事务框架。这两种解决方案都是在业务层实现的。业务开发者有足够的控制权,可以结合SOA框架进行架构,包括Dubbo、SpringCloud等。
