真是绝了,做了这么多年程序员第一次搞懂微服务架构的数据一致性

软件架构场景之—— 数据同步:如何解决微服务之间的数据依赖问题? 业务场景 曾经设计的一个供应链系统中,存在商品、销售订单、采购这三个服务,它们的主数据的部分结构如下所示 在设计这个供应链系统时,我们需要满足以下两个需求 根据商品的型号/分类/生成年份/编码等查找订单; 根据商品的型号/分类/生成年份/编码等查找采购订单 初期的方案是这样设计的:严格按照的微服务划分原则将商品相关的职责存放在商品系统中。因此,在查询订单与采购单时,如果查询字段包含商品字段,需要按照如下顺序进行查询 先根据商品字段调用商品的服务,然后返回匹配的商品信息; 在订单或采购 阅读详情

微服务架构的数据一致性

微服务架构下,最好的分布式数据一致性解决方案就是尽量避免分布式事务,然而,在很多场景下,分布式事务是难以避免的。在金融、电信领域中,很多业务场景要求数据的强一致性,同时要保证服务的可扩展性和可靠性。如何保证分布式事务下的数据一致性成为微服务架构的一个重要课题和难点。

解决方案概览

在工程领域,分布式事务的讨论主要聚焦于强一致性和最终一致性的解决方案。常见的分布式事务有基于XA协议的两阶段(2PC)提交模式,以及改良版本的三阶段(3PC)提交模式。Java事务编程接口(Java Transaction API,JTA)和Java事务服务(Java TransactionService,JTS)正是基于XA协议的实现。分布式事务包括事务管理器TM(Transaction Manager)和一个或多个支持XA协议的资源管理器RM(Resource Manager)。在微服务架构下,我们倾向使用最终一致性的方案。下面将介绍2PC模式、TCC模式、Saga模式等。

● 2PC模式,分布式事务比较典型的解决方案,但是对于微服务架构而言,可能这一方案并不适用,主要原因是不同微服务可能使用的数据存储类型不同。如果使用的NoSQL不支持事务的数据库,那么事务根本无法实现2PC模式。此外,2PC模式本身也存在同步阻塞、单点故障和性能问题。

● TCC模式,相比2PC模式,具有更强的灵活性和性能优势,TCC模式本质上是基于服务层的2PC编程模式,把事务从数据库层的事务操作逻辑抽象到业务服务层,通过服务层业务逻辑实现服务的补偿模式。

● Saga模式,核心理念是将事务切分成一组依次执行的短事务,也可以理解成基于业务补偿逻辑实现分布式下的高性能分布式事务模式。较之基于单一数据库资源访问的本地事务,分布式事务的应用架构更为复杂。在不同的分布式应用架构下,实现一个分布式事务要考虑的问题并不完全一样,比如对多资源的协调、事务的跨服务传播、业务的侵入性、隔离性等,实现机制也是复杂多变的。

● 可靠消息模式的解决方案是使用消息队列进行系统间的解耦。

由上游服务发起事件,通过消息队列传递到下游服务,下游服务接收到消息后进行事件消费,最终完成业务,达到数据一致。为了解决消息队列及上下游服务的不可靠性,通常还会借助额外的事件表和定时器辅助完成数据补偿。

 两阶段提交模式

2PC(两阶段提交)

2PC是一个非常经典的强一致、中心化的通过原子提交来实现分布式事务一致性管理的协议。这里所说的中心化指协议中有两类节点:

中 心 化 协 调 者 节 点 ( Coordinator ) 和 N 个 参 与 者 节 点(Participant)。当一个事务跨越多个节点时,为了保持事务的ACID特性,需要协调者统一掌控所有节点(又称作参与者)的操作结果,并最终指示这些节点是否要把操作结果进行真正的提交或回滚。

2PC将整个事务流程分为两个阶段:准备阶段(Prepare Phase)和提交阶段(Commit Phase)。整个事务过程由事务管理器和参与者组成,事务管理器负责决策整个分布式事务的提交和回滚,事务参与者负责自己本地事务的提交和回滚。大部分关系数据库,如Oracle、MySQL,都支持两阶段提交协议。下面是计算机数据库进行两阶段提交的说明。

● 准备阶段:事务管理器为每个参与者准备(Prepare)消息,每个参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有被提交。(Undo日志记录修改的数据,用于数据回滚;Redo日志记录修改后的数据,用于提交事务后写入数据文件)。

● 提交阶段:如果事务管理器收到了参与者执行失败或者超时的消息,则直接向每个参与者发送回滚消息;否则,发送提交消息。参与者根据事务管理器的消息执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。

2PC的算法思路可以概括为:参与者将操作成败的结果通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是执行提交操作还是回滚操作。两阶段提交就是分成两个阶段提交,第一阶段询问各个事务数据源是否准备好,第二阶段才真正将数据提交给事务数据源。但因为2PC协议成本比较高,又有全局锁的问题,性能会比较差。现在我们基本上不会采用这种强一致性解决方案。2PC流程如下图所示。

在2PC中,如果两阶段出现协调者和参与者都宕机的情况,则有可能出现数据不一致的问题,同时还存在着诸如同步阻塞、单点问题、脑裂等问题,所以研究者们在2PC的基础上做了改进,提出了3PC。

3PC(三阶段提交)

3PC是2PC的改进版本,流程如下图所示。

3PC要解决的最关键问题就是协调者和参与者同时宕机的问题,所以3PC将2PC的第一阶段一分为二,形成了由canCommit、preCommit和Commit 3个阶段组成的事务处理协议。

3PC的核心理念是:在询问时并不锁定资源,除非所有参与者都同意了,才开始锁资源。一旦参与者无法及时收到来自协调者的信息,它就会默认执行Commit,而不会一直持有事务资源并处于阻塞状态,但是这种机制也会导致数据一致性问题。3PC在2PC的基础上做了如下改进:

● 增加了超时机制。

● 在两阶段之间插入了准备阶段。

TCC补偿模式

关于TCC(Try-Confirm-Cancel)的概念,最早是由Pat Helland在2007年发表的一篇名为Lifebeyond Distributed Transactions:anApostate’s Opinion的论文中提出的。在该论文中,TCC还是以
Tentative-Confirmation-Cancellation命名的。

TCC的核心思想是:针对每个操作都要注册一个与其对应的确认和补偿(撤销)操作。TCC事务处理流程和2PC类似,不过2PC通常都在跨库的DB层面,而TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。

TCC模式的工作原理

TCC模式的工作原理如下图所示。TCC将一个完整的事务提交分为Try、Confirm、Cancel 3个操作。

● Try:预留业务资源/数据效验。

● Confirm:确认执行真正要执行的业务,如果所有事务参与者的Try操作都执行成功了,就会调用所有事务参与者的Confirm操作,确认资源。Confirm操作满足幂等性,要求具备幂等设计,Confirm失败后需要进行重试。

● Cancel:取消执行,如果有事务参与者在Try阶段执行失败,就调用所有已成功执行Try阶段的参与者的Cancel方法,释放Try阶段占用的资源。Cancel操作满足幂等性,Cancel阶段的异常和Confirm阶段的异常处理方案基本上一致。

下面是实际模拟用户下单的一个业务场景使用TCC模式的主要流程图。TCC第一阶段是图中的粗线条部分,这个阶段需要分别执行订单服务和库存服务的Try事务,预留必需的业务资源;在第一阶段执行成功后,就会进入第二阶段的Confirm操作,如果不成功,则进行Cancel操作。

TCC的优点和缺点

● TCC的优点:让应用自己定义数据库操作的粒度,使降低锁冲突、提高吞吐量成为可能。TCC的特点在于业务资源检查与加锁,一阶段进行校验,锁定资源,如果第一阶段都成功,则第二阶段对锁定资源进行交易逻辑,否则对锁定资源进行释放,这样就避免了数据库两阶段提交中的锁冲突和长事务低性能风险。

● TCC的缺点:业务逻辑的每个分支都需要实现Try、Confirm、Cancel 3个操作,应用侵入性较强,改造成本高。另外,实现难度较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,Confirm和Cancel接口必须实现幂等。

TCC的开源解决方案

目前,国内的蚂蚁金服主要采用TCC模式进行分布式事务管理。下面总结了常用的TCC开源分布式管理框架:

● Seata

● Tcc-transaction

● Hmily

● ByteTCC

● EasyTransaction

Saga长事务模式

1987年,普林斯顿大学的Hector Garcia-Molina和Kenneth Salem发表了一篇论文:Sagas[3]。这篇论文提出了使用Saga机制作为分布式事 务 的 替 代 品 , 以 解 决 长 时 间 运 行 的 分 布 式 事 务 ( long-LivedTransaction,LLT)问题,LLT指长时间持有数据库资源的长活事务。

该论文认为业务过程经常由很多步骤组成,每一个步骤都涉及一个事务,如果将这些事务组成一个分布式事务,就可以实现总体一致。然而在长时间运行的分布式事务中,使用分布式事务会影响效率和系统的并发处理能力,因为在执行分布式事务时会有锁产生。Saga通过确保每一个业务过程都有修正事务来减少系统对分布式事务的依赖。这种在业务流程中执行修正事务的方式最终保证了系统数据一致性。

每一个LLT的Saga都由一系列sub-transaction Ti组成,每个Ti都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果。同时Saga定义了两种恢复策略:

● 向后恢复模式(Backward Recovery),在这种模式下,每一个内部子事务都有一个对应的补偿事务,如果任一子事务失败,则将撤销之前所有成功的sub-transaction,使整个Saga的执行结果都撤销。

● 向前恢复模式(Forward Recovery),这种模式假设每个子事务最终都会成功,适用于必须要成功的场景,执行顺序如下:T1,T2,…,Tj(失败),Tj(重试),…,Tn,其中Tj是发生错误的sub-transaction,此时执行重试逻辑,在该模式下不需要执行补偿事务。

微服务架构大师Chris Richardson在介绍微服务架构与数据一致性技术时,提出了Saga模式,将微服务中的分布式事务划分为一组小的事务,所划分事务或者全部提交,或者全部回滚。强调在微服务中,每个单独的微服务都可以确保ACID,因为每一个微服务都具备自己的数据库,但是,Saga模式作为整体并不保证隔离性,所以需要对异常情况进行补偿操作。

在Saga模式中,为了保障事务提交和回滚,应使用事务日志结尾和消息传递的方式。在发消息之前,将消息写入本地数据库,这就是所谓的事务日志结尾。它的作用是当新的日志到来时,可以直接发布,这样就可以强化ACD特性;同时Saga模式中子模块之间的消息通信建议采用消息传递方式,因为和HTTP通信协议相比,消息传递方式具备持久性。

Saga模式的工作原理

在微服务架构下,Saga存在两种协调模式。

● 编排模式(Choreography)

这种模式在微服务之间传递Saga,没有重协调器,每个微服务都监听其他服务,并决定采取行动。这种模式最大的优势就是简单,容易理解,参与者之间松散耦合,对于参与者较少的情况,适合采用这种模式。下面是使用编排模式的分布式事务时序图,实线表示消息发布,虚线表示订阅。

事件执行顺序如下:

(1)订单服务(Order Service)在Approval_pending状态下创建了订单,并发布订单创建事件。

(2)库存服务(Inventory Service)消费订单创建事件,在Inventory_pending状态下验证订单,订单出库,并创建InventoryCreate Event。

(3)账户服务(Account Service)消费订单创建事件并进入等待的状态。

(4)账户服务消费Inventory Event,收取费用并发布AccountEvent。

( 5 ) 库 存 服 务 订 阅 Account Event 商 品 出 库 , 更 改Inventory_pending状态为Accept状态。

(6)订单服务收到Account Event,订单状态改为Approved状态。

(7)如果上述订单服务失败,那么库存服务消费费用收取失败事件,回滚之前库存事务清单,将状态改为拒绝。

(8)如果上述账户服务失败,那么订单服务消费费用收取失败事件,回滚订单状态改为拒绝。

● 编制模式(Orchestrator)

这种模式需要一个集中的服务触发器,跟踪Saga的所有子任务调用情况,根据调用情况来决定是否采用补偿措施。这种中央协调的方式可以减少每一个微服务之间的循环依赖,集成处理事件决策和逻辑排序,也更容易推理Saga组合,保证长事务数据的最终一致性。使用业务流程时,可以定义一个控制类,其唯一职责是告诉Saga参与者该做什么。Saga控制使用命令或异步回复样式交互与参与者进行通信。

下面是使用编制模式的分布式事务时序图,实线表示消息request,虚线表示消息reply。

事件执行顺序如下:

(1)订单服务(Order Service)创建一个订单和一个订单控制器:Saga Orchestrator。

(2)Saga Orchestrator向库存服务(Inventory Service)发送一个创建订单命令。

(3)库存服务回复库存出库命令。

(4)Saga Orchestrator向账号服务(Account Service)发送一个费用收取命令。

(5)账号服务回复订单费用扣除命令。

(6)Saga Orchestrator向订单服务发送一个订单已批准命令。

说明:基于业务流程,Saga编制的每个步骤都包括更新数据库和发布消息的服务,例如订单服务持久化和创建Saga Orchestrator,并向第一个Saga参与者发送消息。第一个Saga参与者是库存服务,通过更新数据库回复消息。然后订单服务通过更新Saga协调器的状态向下一个Saga参与者发送命令,并处理参与者的命令回复响应,服务必须使用事务性的消息传递,以便自动更新数据并发布消息。

编排模式与编制模式

微服务中推荐使用编制模式。相比编排模式,编制模式有下面几个优势。

● 编制模式有更简化的依赖关系。编排模式中,服务之间需要掌握相互依赖关系,而且针对不同的异常,可能还需要采用不同的补偿措施,Saga Orchestrator则调用Saga参与者,所有参与者之间是不需要了解Orchestrator的实现细节的。

● 每个服务只需要暴露各自的API供Orchestrator调用即可,因此每个微服务之间有较少耦合,可以降低每个参与者的复杂度。

● 可以使用Saga Orchestrator更加方便地增加或减少Saga分布式事务逻辑,不需要改变每一个参与者的Saga内部事务。同时可以基于Orchestrator进行关注点分离,并简化业务,操作更加容易、方便。

Saga与ACID

Saga不提供对整体隔离性的保证。一个长事务划分为若干本地事务,在本地事务提交后,整个Saga未完成之前,其他服务可以访问“未完成”的中间状态事务数据;Saga协调器实现原子特性,通过日志实现持久性;通过本地日志与Saga日志保证事务一致性。所以Saga模式只支持ACD,不提供隔离性的保证。

Saga与2PC的区别

Saga与2PC最主要的区别在于,2PC是一个强一致性的分布式事务模式,Saga和TCC都通过应用服务层牺牲ACID特性来实现事务的最终一致性,Saga和TCC可以理解为分布式环境下通过事务补偿模式的事务控制模型,只是Saga和TCC有各自不同的实现策略。

Saga与TCC的区别

Saga和TCC最大的区别在于,Saga没有预留资源,而是直接提交到数据库,Saga比TCC少了一步Try操作,无论最终事务成功或失败,TCC都需要与事务参与方交互两次。而Saga在事务成功的情况下只需要与事务参与方交互一次。如果事务失败,则需要采取补偿事务的方式进行回滚。

Saga的开源解决方案

Saga为相互独立、自治的微服务提供了一种分布式网络场景下的数据一致性的解决方案。下面是一些Saga模式的开源解决方案,由于篇幅所限,这里不再赘述方案的实现细节。

● ServiceComb Saga

● Axon Framework

● Eventuate-tram-sagas

可靠消息模式

可靠消息模式主要采用一个可靠的消息中间件作为中介,事务的发起方在完成本地事务后向可靠的消息中间件发起消息,事务消费方在收到消息后处理消息,该方案强调的是双方最终的数据一致性。

如下图所示,订单服务将消息发送给订单服务队列,库存服务监听订阅了订单服务的消息队列,并从消息队列中消费信息。由此可以看到,从事务的发起方到消息中间件,再到事务的消费方,中间都会通过网络,由于网络的不可靠性,导致分布式事务的数据不一致性。

基于这种网络的不可靠性,依靠本地消息表和可靠消息队列的模式可以解决分布式事务的不一致性。而这种模式,对业务的侵入性相对较小,在实现复杂度上也比较可控,国内很多互联网公司都采用了可靠消息模式来解决分布式事务的一致性问题。而这一方案的提出和思路来源于Ebay,之后这种模式在业内被广泛使用。这种分布式事务模式的本质是本地事务+可靠消息,以达到最终事务的一致性。我们来看可靠消息模式的具体实现原理,如下图所示。

可靠消息模式的思想是:事务的发起方需要额外创建一个本地消息表,将本地消息表和业务数据放在同一个事务中提交执行。也就是说,二者要么同时成功,要么同时失败。例如,将订单创建事务和库存验证及库存出库事件消息日志放入同一个事务中,下面是伪代码举例:

从上述伪代码可知,本地数据库与库存出库事务处于同一事务中,二者的绑定操作具备了原子性,工作时序如下:

(1)本地事务提交后,可以使用触发的方式对本地消息表进行查询和消息推送,或者使用定时器的方式轮询本地消息表进行消息推送。

(2)消费者的消息可以使用可靠的消息中间件机制,例如RabbitMQ中的ACK(消息确认机制)保证消费者可以一定消费到消息;

又如库存服务在接收到消息并且完成消息业务处理后,回复ACK,说明此时库存服务已经正确同步数据;如果库存服务没有回复ACK,则消息中间件在没收到ACK消息时,将保留消息,并重复投递此消息。

(3)当消息中间件反馈消费成功后,库存服务可以回调一个订单服务的确认API,这时订单服务可以从本地事务表中删除对应的消息队列。

(4)在订单服务中,如果定时任务重复把本地事务表中的消息发到库存服务,则需要消息消费方(库存服务)提供消息的幂等性支持。

幂等性

简单来说,幂等性的概念就是:除了错误或者过期的请求(换言之就是成功的请求),无论多次调用还是单次调用,最终得到的效果都是一致的。通俗来说,只要有一次调用成功,再采用相同的请求参数,无论调用多少次(重复提交),都应该返回成功。

例如库存服务对外提供服务接口,必须承诺实现接口的幂等性,这一点在分布式系统中极其重要。

● 对于HTTP调用,承诺幂等性可以避免表单或者请求操作重复提交,造成业务数据重复。

● 对于异步消息调用,承诺幂等性通过对消息去重处理也是为了避免重复消费造成业务数据重复。

下面是几种常用的幂等性处理设计方案。

● 数据库表设计对逻辑上唯一的业务键唯一索引,这是在数据库层面做最后的保障。

● 业务逻辑上的防重,例如创建订单的接口,首先通过订单号查询库表中是否已经存在对应的订单,如果存在,则不做处理,直接返回成功。

补偿方案

在可靠消息事务方案中,事务发起方需要确保消息发送到消息队列中,而消息队列成为系统的瓶颈。当消息队列异常,或者消息消费失败导致数据不一致时,需要采取补偿措施,而常用的补偿方案由消息消费方负责。之所以使用消费方补偿模式有下面两个主要理由:

● 一般来说,数据不一致大概率发生在消息消费异常场景,如果由消息发送方补偿错误,往往无法解决消费异常问题;同时一个消息可能存在多个消费方,如果消息补偿模块在所有上游服务中编写,可能无法满足所有消费方的异常补偿场景。

● 当消费方出现问题时,需要定位事务发起方,然后才能通过上游来补偿,这种方式会增加处理生产问题的复杂度。

异步消息交互时,采取的补偿措施通常统一由消息消费方实现,这种方式将类似本地事件表的方式,在消息消费失败后,将失败消息写入本地数据库,然后启动定时任务进行重试,当达到重试上限时,进行预警和人工干预。

RabbitMQ可靠消息传输实践

在众多消息队列中,RabbitMQ最重要的特性就是将消息的可靠性作为传输消息考虑的第一要素。目前,RabbitMQ已经成为金融行业中消息队列的标配。我们将通过RabbitMQ的几个关键因素讲解RabbitMQ如何保证消息的可靠传输。RabbitMQ流程如下图所示。

确认机制

网络异常、机器异常、程序异常等多种情况都可能导致业务丢失消息。对消息进行确认可以解决消息的丢失问题,确认成功意味着消息已被验证并被正确处理。确认机制能用在两个方向:允许消费者告诉服务器(Broker)已经收到了消息,也允许服务器告诉生产者接收到了消息。前者就是我们常说的消费者ACK,后者就是我们常说的生产者Confirm。

RabbitMQ使用生产者消息确认、消费者消息确认机制来提供可靠交付功能。

● 生产者消息确认:生产者向RabbitMQ发送消息后,等待它回复确认成功;否则生产者向RabbitMQ重发该消息。此过程可以异步进行,生产者持续发送消息,RabbitMQ将消息批量处理后再回复确认;生产者通过识别确认返回中的ID来确定哪些消息被成功处理。开启生产者消息确认机制:

● 消费者消息确认:RabbitMQ向消费者投递消息后,等待消费者回复确认成功;否则RabbitMQ重新向消费者投递该消息。该过程同样可以异步处理,RabbitMQ持续投递消息,消费者批量处理完后回复确认。可以看出,RabbitMQ/AMQP提供的是“至少一次交付”(at-least-once delivery)的策略,异常情况下消息会被重复投递或消费。开启消费者消息确认机制:

持久化机制

设置交换机、队列和消息都为持久化,它可以在服务器重启时保证消息不丢失信息,集群节点提供冗余能力,对解决Broker的单点故障至关重要。在RabbitMQ集群中,所有的定义都可以被冗余处理,例如交换器和绑定关系等,而队列只存在于一个节点上。对于队列而言,可以通过配置把队列镜像到多个节点上。

● 交换机的持久化(通过查看源码易知,默认是支持持久化的),代码如下:

● 队列的持久化(通过查看源码易知,默认是支持持久化的),代码如下:

● 消 息 的 持 久 化 。 当 我 们 使 用 RabbitTemplate 调 用convertAndSend(String exchange,String routingKey,final Object object)方法时,默认是持久化模式。

生产者

当使用确认机制时,生产者从连接或者Channel故障中恢复过来,会重发没有被Broker确认签收的消息。如此一来,消息就可能被重复发送,可能是由于网络故障等原因,Broker发送了确认,但是生产者没有收到而已。或者消息根本就没有发送到Broker。正因为生产者为了可靠性可能会重发消息,所以在消费者消费消息处理业务时,还需要去重,或者对接收的消息做幂等处理(推荐幂等处理)。生产者增加确认机制非常简单,Channel开启Confirm模式,然后增加监听即可。

说 明 : RabbitMQ 还 有 事 务 机 制 ( txSelect 、 txCommit 、txRollback),也能保障消息的发送。不过事务机制是“同步阻塞”的,所以不推荐使用。而Confirm模式是“异步”机制。通过事务机制与Confirm模式的TPS性能对比,我们可以很明显地看到,事务机制是性能最差的。

RabbitMQ支持的4种交换器类型中,只有fanout模式不存在路由不到队列的情况。因为它会自动路由到所有队列中,跟绑定Key没有任何关系。所以,在满足业务的前提下,笔者建议,尽可能使用fanout模式的类型交换器。

DLX(Dead Letter Exchange,死信邮箱或死信交换机)就是一个普通的交换机,与一般的交换机没有任何区别。当消息在一个队列中变成死信时,通过这个交换机将死信发送到死信队列中。

我们可以通过DLX来解决这个问题,假设一些消息没有被消费,那么它就会被转移到绑定的DLX上。对于这类消息,我们消费并处理死信队列即可。

消费者

只有消费者确认的消息,RabbitMQ才会删除它,不确认就不会被删除。所以,在消费端,建议关闭自动确认机制。应该在收到消息、处理完业务后,手动确认消息。消费者手动确认消息的实现代码如下:

注意上面方法中void basicAck(long deliveryTag,booleanmultiple)的第二个参数multiple。要说明这个参数的含义,首先需要清楚“deliveryTag”概念,即投递消息的唯一标识符,它是一个“单调递增”的Long型正整数。假设此次basicAck的tag为123456,如果 multiple=false , 则 表 示 只 确 认 签 收 这 一 条 消 息 。 如 果multiple=true,则表示确认签收tag小于或等于123456的所有消息。

小结

在微服务架构下,我们强调要根据微服务的数据类型和业务场景选择合适的后端数据存储类型。对于微服务架构下分布式应用中的数据一致性管理,不推荐使用分布式事务,微服务数据架构通过放弃分布式网络的强一致性,来提升微服务之间的交互性能。另外,在微服务数据架构中,我们介绍了常见的TCC、Saga、可靠消息模式,可以作为保证数据之间最终一致性的解决方案。

本文给大家讲解的内容是微服务数据架构,微服务架构的数据一致性

微服务架构中的数据一致性:如何保证最终一致性与强一致性 微服务架构中,数据一致性问题是一个无法回避的挑战。强一致性适用于关键业务场景,但可能牺牲性能。最终一致性适用于性能要求高、可容忍短暂不一致的场景。合理设计事务管理、利用消息队列和分布式锁等工具,可以帮助我们在一致性与性能之间找到最佳平衡点。在你的项目中,可以尝试不同的模式,找到最适合的解决方案! 阅读详情

相关推荐

做了这么多年程序员第一次搞懂微服务架构的数据一致性

在不同的分布式应用架构下,实现一个分布式事务要考虑的问题并不完全一样,比如对多资源的协调、事务的跨服务传播、业务的侵入性、隔离性等,实现机制也是复杂多变的。微服务架构大师Chris Richardson在介绍微服务架构与数据一致性技术时,提出了Saga模式,将微服务中的分布式事务划分为一组小的事务,所划分事务或者全部提交,或者全部回滚。强调在微服务中,每个单独的微服务都可以确保ACID,因为每一个微服务都具备自己的数据库,但是,Saga模式作为整体并不保证隔离性,所以需要对异常情况进行补偿操作。

mfmfmfo的博客 390

微服务--数据一致性

本篇文章讲解微服务数据一致性相关的知识。

喵叔 2579

一笔订单的一致性远征:两阶段提交的困局、CAP与BASE的取舍与六种柔性事务模式的工程选型

微服务拆分后,单体里一个本地事务就能保证的原子性被服务边界切断,跨服务丢失、幽灵、重复与悬挂随之出现。本文跟随一笔订单,先剖析XA与两阶段、三阶段提交,用离散事件仿真对比二者在协调者宕机与网络分区下的锁持有和数据分叉;再借CAP、PACELC、一致性模型光谱与BASE思想划定理论边界;随后逐一拆解可靠事件、补偿、Saga、TCC、最大努力通知与人工干预六种柔性事务模式的思路、关键点、实现策略与实测数据;最后给出横向对比、选型决策路径与下单链路组合方案,说明最终一致需要幂等、可观测与对账兜底。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 170万+

这12张手绘图,让我彻底搞懂了微服务架构!

点击上方 "程序员小乐"关注,星标或置顶一起成长每天凌晨00点00分,第一时间与你相约每日英文I've learned that people will fo...

程序员小乐 1313

WSL2 + Open3D点云可视化踩坑全记录:从GLFW报错到VcXsrv配置,一篇讲清所有‘坑’

本文详细解析了在WSL2环境中使用Open3D进行点云可视化时遇到的常见问题,特别是GLFW报错和VcXsrv配置问题。通过系统性的解决方案和高级配置技巧,帮助开发者彻底解决图形显示、驱动加载和库版本冲突等难题,实现高效的点云可视化。

weixin_30627341的博客 99

从ADRV9002到ADRV9003:手把手教你移植FPGA驱动,避开那些官方没说的坑

本文详细介绍了从ADRV9002到ADRV9003的FPGA驱动移植实战指南,重点解析了两者在硬件架构上的关键差异,如通道配置和DPD功能的移除。通过提供完整的移植步骤、调试技巧和性能优化建议,帮助工程师避开官方文档未提及的常见陷阱,实现高效驱动开发。

weixin_28738397的博客 173

最全腾讯T4架构师用这12张手绘图,轻松带你搞懂微服务架构!太厉害了,java面试问到项目难点例子

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)

jiameih的博客 683

PC网站实现微信扫码登录功能(二)

上篇我们介绍了怎么申请账号以及最后拿到appId和appSecret,如果有不清楚的可以看一下 PC网站实现微信扫码登录功能(一) 下面我就来说一下怎样在PC端怎么实现扫码登录功能。 一、进入官方文档网站 https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html 其实官网对过程描述的已经非常细致了。并且提供了两种扫码方式。一种是跳转二维码扫描页面,另一种是内嵌式二维码。大家可以

qq_35771266的博客 3万+

将半年来的精品原创技术文章整理成了电子书送给大家

作者简介:曾任职于阿里巴巴,每日优鲜等互联网公司,任技术总监。15年电商互联网经历。将多年的微服务经历,高并发经历,JVM调优经历整理成了电子书,共130页。两天读完,月薪涨1万,问题不...

ftcool的博客 320

把“思考”塞进 1 KB:我用纯 C 语言给单片机手搓了一个微型 Transformer 推理引擎

摘要:本文探讨了在64KB SRAM的MCU(如STM32H743)上部署精简Transformer模型的技术方案。通过将6层Transformer压缩至1层(隐藏维度128)、4-bit KV-Cache量化等优化手段,实现了240KB Flash占用和59KB RAM峰值,推理延迟184ms(400MHz Cortex-M7),准确率96.1%。关键创新包括:手工优化矩阵乘(CMSIS-NN)、查表法Softmax/LayerNorm、权重按Tile重排提升Cache命中率(60%→94%)。

qq_74383080的博客 2921

图解链表 —— JAVA中的单链表基本操作

文章目录1. 什么是链表2. 链表的种类3. 创建单链表3.1 创建 Node 类(车厢类)3.2 创建 MySingleList (火车类)4. 单链表的基本操作4.1 增加链表元素4.1.1 在链表头部添加元素4.1.2 在链表任意位置添加元素4.1.3 在链表尾部添加元素4.2 删除链表元素4.2.1 删除第一个节点元素4.2.2 删除任意位置 index 元素4.2.3 删除链表中指定元素的第一个节点4.2.4 删除链表的所有指定元素4.3 查找链表元素4.3.1 判断链表中是否包含元素 data4

AlinaQ05的博客 2501

阿里专家15年经历整理成干货电子书免费送

作者简介:曾任职于阿里巴巴,每日优鲜等互联网公司,任技术总监。15年电商互联网经历。认识二马是5年前的事了,当时他还在阿里巴巴潜心研究技术,是我眼中的技术狂热者。二马很多干货文章,都来自...

weixin_41385912的博客 211

Yolov26 在 Rk3588 上的 Rknn Onnx 部署实践(模型转换)

本文详细介绍了将YOLOv26 ONNX模型转换为RKNN模型的过程,适用于RK3588平台。主要内容包括:1)量化数据集的创建,通过随机抽取最多400张JPG图片生成校准数据集;2)ONNX转RKNN的完整脚本,支持多种量化方式和平台适配;3)模型转换的参考调用方式;4)模型结构对比展示。文章还提供了注意事项,强调量化数据集图片尺寸、mean/std值需与实际推理一致,并给出精度优化建议。该转换流程为YOLOv26模型在RK3588上的高效部署提供了完整解决方案。

qq_64366203的博客 1125

AI时代Java程序员的核心竞争力:JVM调优、并发编程与MySQL深度优化

在软件开发领域,理解底层原理和系统性能优化是工程师的核心价值。从计算机科学基础概念出发,程序运行时的内存管理、并发控制与数据库操作是构建高可用系统的关键技术支柱。JVM(Java虚拟机)作为Java应用的运行环境,其内存模型与垃圾回收机制直接决定了应用的稳定性和吞吐量;并发编程涉及多线程协同与数据一致性,是应对高并发场景的基石;而MySQL等关系型数据库的索引原理、事务隔离与锁机制,则是保障数据准确性与查询效率的关键。这些深度知识构成了解决线上复杂问题(如内存泄漏、死锁、慢查询)的能力,也是当前企业技术选型

weixin_33888907的博客 425

逆向实战:Soul聊天协议加密与Frida绕过检测

本文详细解析了Soul聊天协议逆向分析的全过程,重点介绍了如何利用Frida绕过应用的反调试检测机制。通过静态分析定位关键SO库,并编写Frida脚本动态Hook线程创建函数,成功实现对抗。文章还进一步追踪了基于DES算法的加密流程,并探讨了协议数据结构的解析方法,为移动应用安全研究提供了实战参考。

2w3e4r5t6y的博客 1130

【Claude】Auto mode cannot determine the safety of an action 报错已解决

Claude Code Auto mode 安全分类器报错完整解析,涵盖 temporarily unavailable、could not evaluate、transcript exceeded 三种情况的根因与处理

博客 9946

网易资深Java架构师:jdkjrejvm的区别和联系

前言 作为同时具备高性能、高可靠和高可扩展性的典型键值数据库,Redis不仅功能强大,而且稳定,理所当然地成为了大型互联网公司的首选。 众多大厂在招聘的时候,不仅会要求面试者能简单地使用Redis,还要能深入地理解底层实现原理,并且具备解决常见问题的能力。可以说,熟练掌握Redis已经成为了技术人的一个必备技能。 但是,在学习和使用Redis的过程中,总不可避免地遇见一些棘手的问题,比如: Redis的key和数据结构应该怎么设计?有什么最佳实践? Redis集群如何均衡数据?又如何横向扩展? 怎么保证数

m0_57081562的博客 255

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

互联网大厂面试必问的JVM底层原理,美团阿里Java程序员晒工资被围观

前言 作为同时具备高性能、高可靠和高可扩展性的典型键值数据库,Redis不仅功能强大,而且稳定,理所当然地成为了大型互联网公司的首选。 众多大厂在招聘的时候,不仅会要求面试者能简单地使用Redis,还要能深入地理解底层实现原理,并且具备解决常见问题的能力。可以说,熟练掌握Redis已经成为了技术人的一个必备技能。 但是,在学习和使用Redis的过程中,总不可避免地遇见一些棘手的问题,比如: Redis的key和数据结构应该怎么设计?有什么最佳实践? Redis集群如何均衡数据?又如何横向扩展? 怎么保证数

EDGNB123的博客 1208

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

支付宝前端团队全转Agent:Java后端才是最大赢家

支付宝前端团队解散,全员转Agent了。上月末这条消息直接把技术圈炸穿了。不是缩减。不是优化。整条线换赛道。我第一反应不是震惊。是确认。确认我去年那个决定做对了。先交个底:我今年34,做了8年Java后端,去年年底咬着牙裸辞转了AI应用开发。这一年在Agent的坑里滚了一遍,面了十几家公司,带了两个转过来的同事。今天不说虚的。就借支付宝这件事,跟所有还在犹豫的后端兄弟说句大实话。很多人看到这条消息,脑子里就一个念头:「连支付宝前端都在转了,是不是所有程序员都得搞AI?不是。

Android23333的博客 207

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【时事热点】支付宝前端团队全转Agent:Java后端才是最大赢家

文章摘要(150字): 支付宝前端团队全员转型AI Agent开发,折射出行业技术迁移趋势。后端开发者凭借系统稳定性、异常处理和成本控制等工程经验,在Agent开发中具备天然优势。转型核心在于将后端架构思维融入AI技术,如RAG优化、多级降级等,而非从零学Python。当前企业级Agent人才缺口大,薪资溢价显著,是后端人员抓住红利期的关键窗口。作者分享实战转型路径:从LLM基础应用到RAG全链路实践,再手搓Agent并融入后端工程经验,最终打造可落地的企业级系统。附赠学习资料包,涵盖技术路线、项目模板及面

Android_XG的博客 211
上一篇: JDBC 在性能测试中的应用
下一篇: 新来个技术总监,把DDD落地的那叫一个高级,服气!
Java5658
博客等级 码龄4年 169粉丝 107原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值