分布式事务 Myth丶恋晨 2022-02-13 06:55 306阅读 0赞 ## **什么是分布式事务** ## 分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。 本质上来说,分布式事务就是为了保证不同数据库的数据一致性。 ## [**分布式事务产生的原因**][Link 1] ## 随着互联网快速发展,微服务,SOA 等服务架构模式正在被大规模的使用。 举个简单的例子,一个公司之内,用户的资产可能分为好多个部分,比如余额,积分,优惠券等等。 在公司内部有可能积分功能由一个微服务团队维护,优惠券又是另外的团队维护。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70][] 这样的话就无法保证积分扣减了之后,优惠券能否扣减成功。 同样的,互联网发展得太快了,我们的 MySQL 一般来说装千万级的数据就得进行分库分表。 对于一个支付宝的转账业务来说,你给朋友转钱,有可能你的数据库是在北京,而你的朋友的钱是存在上海,所以我们依然无法保证他们能同时成功。 ![20190427085744900.png][] ## **酸碱平衡** ## ACID能够保证事务的强一致性,即数据是实时一致的。这在本地事务中是没有问题的,在分布式事务中,强一致性会极大影响分布式系统的性能,因此分布式系统中遵循BASE理论即可。但分布式系统的不同业务场景对一致性的要求也不同。如交易场景下,就要求强一致性,此时就需要遵循ACID理论,而在注册成功后发送短信验证码等场景下,并不需要实时一致,因此遵循BASE理论即可。因此要根据具体业务场景,在ACID和BASE之间寻求平衡。分布式事务不可能100%解决,只能尽可能提高成功概率。 ## **两阶段提交协议2PC** ## ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 1][] 分布式系统的一个难点是如何保证架构下多个节点在进行事务性操作的时候保持一致性。为实现这个目的,二阶段提交算法的成立基于以下假设: * 该分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts)。且节点之间可以进行网络通信。 * 所有节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失。 * 所有节点不会永久性损坏,即使损坏后仍然可以恢复。 **1. 第一阶段(投票阶段):** 1. 协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。 2. 参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作) 3. 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。 **2. 第二阶段(提交执行阶段):** 当协调者节点从所有参与者节点获得的相应消息都为”同意”时: 1. 协调者节点向所有参与者节点发出”正式提交(commit)”的请求。 2. 参与者节点正式完成操作,并释放在整个事务期间内占用的资源。 3. 参与者节点向协调者节点发送”完成”消息。 4. 协调者节点受到所有参与者节点反馈的”完成”消息后,完成事务。 如果任一参与者节点在第一阶段返回的响应消息为”中止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时: 1. 协调者节点向所有参与者节点发出”回滚操作(rollback)”的请求。 2. 参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。 3. 参与者节点向协调者节点发送”回滚完成”消息。 4. 协调者节点受到所有参与者节点反馈的”回滚完成”消息后,取消事务。 不管最后结果如何,第二阶段都会结束当前事务。 **二阶段提交看起来确实能够提供原子性的操作,但是不幸的事,二阶段提交还是有几个缺点的:** * 1、同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。 * 2、单点故障。由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题) * 3、数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。相比第一阶段,第二阶段的时间很小很小,所以发生的概率很低。 ## **三阶段提交协议 3PC** ## 与两阶段提交不同的是,三阶段提交有两个改动点。 * 引入超时机制。同时在协调者和参与者中都引入超时机制。 * 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。 也就是说,除了引入超时机制之外,3PC把2PC的准备阶段再次一分为二,这样三阶段提交就有**CanCommit、PreCommit、DoCommit**三个阶段。 **1. CanCommit阶段** 3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。 * 事务询问:协调者向参与者发送CanCommit请求。询问是否可以执行事务提交操作。然后开始等待参与者的响应。 * 响应反馈:参与者接到CanCommit请求之后,正常情况下,如果其自身认为可以顺利执行事务,则返回Yes响应,并进入预备状态。否则反馈No **2. PreCommit阶段** 协调者根据参与者的反应情况来决定是否可以记性事务的PreCommit操作。根据响应情况,有以下两种可能。 假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务的预执行。 * 发送预提交请求:协调者向参与者发送PreCommit请求,并进入Prepared阶段。 * 事务预提交:参与者接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。 * 响应反馈:如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。 假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。 * 发送中断请求协调者向所有参与者发送abort请求。 * 中断事务参与者收到来自协调者的abort请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。 **3. doCommit阶段** 该阶段进行真正的事务提交,也可以分为以下两种情况。 **3.1 执行提交** * 发送提交请求:协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送doCommit请求。 * 事务提交:参与者接收到doCommit请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。 * 响应反馈;事务提交完之后,向协调者发送Ack响应。 * 完成事务:协调者接收到所有参与者的ack响应之后,完成事务。 **3.2 中断事务** 协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。 * 发送中断: 请求协调者向所有参与者发送abort请求 * 事务回滚: 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。 * 反馈结果: 参与者完成事务回滚之后,向协调者发送ACK消息 * 中断事务: 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。 ## 2PC与3PC的区别 ## 相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。但是这种机制也会**导致数据一致性问题**,因为,由于网络原因,协调者发送的abort响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到abort命令并执行回滚的参与者之间存在数据不一致的情况。 ## **分布式事务的解决方案** ## 分布式事务的解决方案有如下几种: * 全局消息 * 基于可靠消息服务的分布式事务 * TCC * 最大努力通知 ### 方案1:全局事务(DTP模型) ### 全局事务基于DTP模型实现。DTP是由X/Open组织提出的一种分布式事务模型——X/Open Distributed Transaction Processing Reference Model。它规定了要实现分布式事务,需要三种角色: 1、AP:Application 应用系统.它就是我们开发的业务系统,在我们开发的过程中,可以使用资源管理器提供的事务接口来实现分布式事务。 2、TM:Transaction Manager 事务管理器 * 分布式事务的实现由事务管理器来完成,它会提供分布式事务的操作接口供我们的业务系统调用。这些接口称为TX接口。 * 事务管理器还管理着所有的资源管理器,通过它们提供的XA接口来同一调度这些资源管理器,以实现分布式事务。 * DTP只是一套实现分布式事务的规范,并没有定义具体如何实现分布式事务,TM可以采用2PC、3PC、Paxos等协议实现分布式事务。 3、RM:Resource Manager 资源管理器 * 能够提供数据服务的对象都可以是资源管理器,比如:数据库、消息中间件、缓存等。大部分场景下,数据库即为分布式事务中的资源管理器。 * 资源管理器能够提供单数据库的事务能力,它们通过XA接口,将本数据库的提交、回滚等能力提供给事务管理器调用,以帮助事务管理器实现分布式的事务管理。 * XA是DTP模型定义的接口,用于向事务管理器提供该资源管理器(该数据库)的提交、回滚等能力。 * DTP只是一套实现分布式事务的规范,RM具体的实现是由数据库厂商来完成的。 ### **[方案二:TCC事务模型][TCC]** ### 达到事务的效果,要么一起成功,要么一起失败!就要采取TCC分布式事务方案! ### **概念** ### TCC的全称是(Try-Confirm-Cancel)。如下图所示 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 2][] ** ps:TCC又可以被称为两阶段补偿事务,第一阶段try只是预留资源,第二阶段要明确的告诉服务提供者,这个资源你到底要不要,对应第二阶段的confirm/cancel,用来清除第一阶段的影响,所以叫补偿型事务。** 我们先套一个业务场景进去,如下图所示 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 3][] 那对一个订单支付之后,我们需要做下面的步骤: * **更改订单的状态为“已支付”** * **扣减商品库存** * **给会员增加积分** * **创建销售出库单通知仓库发货** 这是一系列比较真实的步骤,无论大家有没有做过电商系统,应该都能理解。 ### **TCC阶段一:Try(预留资源)** ### 首先,订单服务那儿,它的代码大致来说应该是这样子的: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 4][] 在 pay() 那个方法里,你别直接把订单状态修改为已支付!你先把订单状态修改为 UPDATING,也就是修改中的意思。这个状态是个没有任何含义的这么一个状态,代表有人正在修改这个状态罢了。然后呢,库存服务直接提供的那个 reduceStock() 接口里,也别直接扣减库存啊,你可以是冻结掉库存。举个例子: * 本来你的库存数量是 100,你别直接 100 - 2 = 98,扣减这个库存!你可以把可销售的库存:100 - 2 = 98,设置为 98 没问题,然后在一个单独的冻结库存的字段里,设置一个 2。也就是说,有 2 个库存是给冻结了。之后这个数据库的锁将会释放。相比与2pc并发性能有提高。 * 积分服务的 addCredit() 接口也是同理,别直接给用户增加会员积分。你可以先在积分表里的一个预增加积分字段加入积分。比如:用户积分原本是 1190,现在要增加 10 个积分,别直接 1190 + 10 = 1200 个积分啊!你可以保持积分为 1190 不变,在一个预增加字段里,比如说 prepare\_add\_credit 字段,设置一个 10,表示有 10 个积分准备增加。 * 仓储服务的 saleDelivery() 接口也是同理啊,你可以先创建一个销售出库单,但是这个销售出库单的状态是“UNKNOWN”。 也就是说,刚刚创建这个销售出库单,此时还不确定它的状态是什么呢! 上面这套改造接口的过程,其实就是所谓的 TCC 分布式事务中的第一个 T 字母代表的阶段,也就是 Try 阶段。 咱们来一起看看下面这张图,结合上面的文字,再来捋一捋整个过程: ![aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ2NZV0w0aWFKTXFpY3l0d0I1U3Q3aWNtUUpaZldhaWI1V2xpY0p1N3FPMVQ2a3l2SlNPYlZpYmliTWU0dFEvNjQwP3d4X2ZtdD1wbmcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ][] 总结上述过程,如果你要实现一个 TCC 分布式事务,首先你的业务的主流程以及各个接口提供的业务含义,不是说直接完成那个业务操作,而是完成一个 Try 的操作。 ### **TCC 实现阶段二:Confirm** ### 然后就分成两种情况了,第一种情况是比较理想的,那就是各个服务执行自己的那个 Try 操作,都执行成功了,Bingo! 这个时候,就需要依靠 TCC 分布式事务框架来推动后续的执行了。这里简单提一句,如果你要玩儿 TCC 分布式事务,必须引入一款 TCC 分布式事务框架,比如国内开源的 ByteTCC、Himly、TCC-transaction。否则的话,感知各个阶段的执行情况以及推进执行下一个阶段的这些事情,不太可能自己手写实现,太复杂了。 如果你在各个服务里引入了一个 TCC 分布式事务的框架,订单服务里内嵌的那个 TCC 分布式事务框架可以感知到,各个服务的 Try 操作都成功了。 此时,TCC 分布式事务框架会控制进入 TCC 下一个阶段,第一个 C 阶段,也就是 Confirm 阶段。 为了实现这个阶段,你需要在各个服务里再加入一些代码。比如说,订单服务里,你可以加入一个 Confirm 的逻辑,就是正式把订单的状态设置为“已支付”了,大概是类似下面这样子: ![aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ1c3NDhMdEtQYzJzUzE0S1FwMVFKbXpEZlp1RHI4NEdqWnZabGtEUldXR3g1ekl5b2ljUjRSdWcvNjQwP3d4X2ZtdD1wbmcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ][] 库存服务也是类似的,你可以有一个 InventoryServiceConfirm 类,里面提供一个 reduceStock() 接口的 Confirm 逻辑,这里就是将之前冻结库存字段的 2 个库存扣掉变为 0。这样的话,可销售库存之前就已经变为 98 了,现在冻结的 2 个库存也没了,那就正式完成了库存的扣减。 积分服务也是类似的,可以在积分服务里提供一个 CreditServiceConfirm 类,里面有一个 addCredit() 接口的 Confirm 逻辑,就是将预增加字段的 10 个积分扣掉,然后加入实际的会员积分字段中,从 1190 变为 1120。 仓储服务也是类似,可以在仓储服务中提供一个 WmsServiceConfirm 类,提供一个 saleDelivery() 接口的 Confirm 逻辑,将销售出库单的状态正式修改为“已创建”,可以供仓储管理人员查看和使用,而不是停留在之前的中间状态“UNKNOWN”了。 ### **TCC 实现阶段三:Cancel** ### 好,这是比较正常的一种情况,那如果是异常的一种情况呢?举个例子:在 Try 阶段,比如积分服务吧,它执行出错了,此时会怎么样?那订单服务内的 TCC 事务框架是可以感知到的,然后它会决定对整个 TCC 分布式事务进行回滚。 也就是说,会执行各个服务的第二个 C 阶段,Cancel 阶段。同样,为了实现这个 Cancel 阶段,各个服务还得加一些代码。 首先订单服务,它得提供一个 OrderServiceCancel 的类,在里面有一个 pay() 接口的 Cancel 逻辑,就是可以将订单的状态设置为“CANCELED”,也就是这个订单的状态是已取消。 * 库存服务也是同理,可以提供 reduceStock() 的 Cancel 逻辑,就是将冻结库存扣减掉 2,加回到可销售库存里去,98 + 2 = 100。 * 积分服务也需要提供 addCredit() 接口的 Cancel 逻辑,将预增加积分字段的 10 个积分扣减掉。 * 仓储服务也需要提供一个 saleDelivery() 接口的 Cancel 逻辑,将销售出库单的状态修改为“CANCELED”设置为已取消。 然后这个时候,订单服务的 TCC 分布式事务框架只要感知到了任何一个服务的 Try 逻辑失败了,就会跟各个服务内的 TCC 分布式事务框架进行通信,然后调用各个服务的 Cancel 逻辑。 大家看看下面的图,直观的感受一下: ![aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ0k2QW5GZFdoRDdqMUs5dVlHelY5N2ljcWNycGxnZ3YxUWNqNXdvdU1KMHRYZkRvanNybUpjNHcvNjQwP3d4X2ZtdD1wbmcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ][] 问题还没完,**万一某个服务的 Cancel 或者 Confirm 逻辑执行一直失败怎么办呢?** 那也很简单,TCC 事务框架会通过活动日志记录各个服务的状态。举个例子,比如发现某个服务的 Cancel 或者 Confirm 一直没成功,会不停的重试调用它的 Cancel 或者 Confirm 逻辑,务必要它成功!当然了,如果你的代码没有写什么 Bug,有充足的测试,而且 Try 阶段都基本尝试了一下,那么其实一般 Confirm、Cancel 都是可以成功的! 最后,再给大家来一张图,来看看给我们的业务,加上分布式事务之后的整个执行流程: ![aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ013eEw1eTY3aWJDWGE4TDQ1RmhmZXRvZU5pY1dJczgxbGZFS1daN0VXY0lFODVLNTlwS2dLTUh3LzY0MD93eF9mbXQ9cG5nJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE][] **补偿方案:针对二阶段,采用重试+日志+人工介入来保证数据一致性。TCC开源框架:github上的byteTCC、tcc-transcational等。** ### 方案3:基于可靠消息服务的分布式事务 ### 这种实现分布式事务的方式需要通过消息中间件来实现。假设有A和B两个系统,分别可以处理任务A和任务B。此时系统A中存在一个业务流程,需要将任务A和任务B在同一个事务中处理。下面来介绍基于消息中间件来实现这种分布式事务。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 5][] * 在系统A处理任务A前,首先向消息中间件发送一条消息 * 消息中间件收到后将该条消息持久化,但并不投递。此时下游系统B仍然不知道该条消息的存在。 * 消息中间件持久化成功后,便向系统A返回一个确认应答; * 系统A收到确认应答后,则可以开始处理任务A; * 任务A处理完成后,向消息中间件发送Commit请求。该请求发送完成后,对系统A而言,该事务的处理过程就结束了,此时它可以处理别的任务了。 * 但commit消息可能会在传输途中丢失,从而消息中间件并不会向系统B投递这条消息,从而系统就会出现不一致性。这个问题由消息中间件的事务回查机制完成,下文会介绍。 * 消息中间件收到Commit指令后,便向系统B投递该消息,从而触发任务B的执行; * 当任务B执行完成后,系统B向消息中间件返回一个确认应答,告诉消息中间件该消息已经成功消费,此时,这个分布式事务完成。 > 上述过程可以得出如下几个结论: > 1. 消息中间件扮演者分布式事务协调者的角色。 > 2. 系统A完成任务A后,到任务B执行完成之间,会存在一定的时间差。在这个时间差内,整个系统处于数据不一致的状态,但这短暂的不一致性是可以接受的,因为经过短暂的时间后,系统又可以保持数据一致性,满足BASE理论。 上述过程中,如果任务A处理失败,那么需要进入回滚流程,如下图所示: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 6][] * 若系统A在处理任务A时失败,那么就会向消息中间件发送Rollback请求。和发送Commit请求一样,系统A发完之后便可以认为回滚已经完成,它便可以去做其他的事情。 * 消息中间件收到回滚请求后,直接将该消息丢弃,而不投递给系统B,从而不会触发系统B的任务B。 > 此时系统又处于一致性状态,因为任务A和任务B都没有执行。 上面所介绍的Commit和Rollback都属于理想情况,但在实际系统中,Commit和Rollback指令都有可能在传输途中丢失。那么当出现这种情况的时候,消息中间件是如何保证数据一致性呢?——答案就是超时询问机制。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 7][] 系统A除了实现正常的业务流程外,还需提供一个事务询问的接口,供消息中间件调用。当消息中间件收到一条事务型消息后便开始计时,如果到了超时时间也没收到系统A发来的Commit或Rollback指令的话,就会主动调用系统A提供的事务询问接口询问该系统目前的状态。该接口会返回三种结果: * 提交:若获得的状态是“提交”,则将该消息投递给系统B。 * 回滚:若获得的状态是“回滚”,则直接将条消息丢弃。 * 处理中:若获得的状态是“处理中”,则继续等待。 > 消息中间件的超时询问机制能够防止上游系统因在传输过程中丢失Commit/Rollback指令而导致的系统不一致情况,而且能降低上游系统的阻塞时间,上游系统只要发出Commit/Rollback指令后便可以处理其他任务,无需等待确认应答。而Commit/Rollback指令丢失的情况通过超时询问机制来弥补,这样大大降低上游系统的阻塞时间,提升系统的并发度。 下面来说一说消息投递过程的可靠性保证。 当上游系统执行完任务并向消息中间件提交了Commit指令后,便可以处理其他任务了,此时它可以认为事务已经完成,接下来消息中间件一定会保证消息被下游系统成功消费掉!那么这是怎么做到的呢?这由消息中间件的投递流程来保证。 消息中间件向下游系统投递完消息后便进入阻塞等待状态,下游系统便立即进行任务的处理,任务处理完成后便向消息中间件返回应答。消息中间件收到确认应答后便认为该事务处理完毕! 如果消息在投递过程中丢失,或消息的确认应答在返回途中丢失,那么消息中间件在等待确认应答超时之后就会重新投递,直到下游消费者返回消费成功响应为止。当然,一般消息中间件可以设置消息重试的次数和时间间隔,比如:当第一次投递失败后,每隔五分钟重试一次,一共重试3次。如果重试3次之后仍然投递失败,那么这条消息就需要人工干预。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 8][] ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 9][] > 有的同学可能要问:消息投递失败后为什么不回滚消息,而是不断尝试重新投递? 这就涉及到整套分布式事务系统的实现成本问题。 我们知道,当系统A将向消息中间件发送Commit指令后,它便去做别的事情了。如果此时消息投递失败,需要回滚的话,就需要让系统A事先提供回滚接口,这无疑增加了额外的开发成本,业务系统的复杂度也将提高。对于一个业务系统的设计目标是,在保证性能的前提下,最大限度地降低系统复杂度,从而能够降低系统的运维成本。 > 不知大家是否发现,上游系统A向消息中间件提交Commit/Rollback消息采用的是异步方式,也就是当上游系统提交完消息后便可以去做别的事情,接下来提交、回滚就完全交给消息中间件来完成,并且完全信任消息中间件,认为它一定能正确地完成事务的提交或回滚。然而,消息中间件向下游系统投递消息的过程是同步的。也就是消息中间件将消息投递给下游系统后,它会阻塞等待,等下游系统成功处理完任务返回确认应答后才取消阻塞等待。为什么这两者在设计上是不一致的呢? 首先,上游系统和消息中间件之间采用异步通信是为了提高系统并发度。业务系统直接和用户打交道,用户体验尤为重要,因此这种异步通信方式能够极大程度地降低用户等待时间。此外,异步通信相对于同步通信而言,没有了长时间的阻塞等待,因此系统的并发性也大大增加。但异步通信可能会引起Commit/Rollback指令丢失的问题,这就由消息中间件的超时询问机制来弥补。 那么,消息中间件和下游系统之间为什么要采用同步通信呢? 异步能提升系统性能,但随之会增加系统复杂度;而同步虽然降低系统并发度,但实现成本较低。因此,在对并发度要求不是很高的情况下,或者服务器资源较为充裕的情况下,我们可以选择同步来降低系统的复杂度。 我们知道,消息中间件是一个独立于业务系统的第三方中间件,它不和任何业务系统产生直接的耦合,它也不和用户产生直接的关联,它一般部署在独立的服务器集群上,具有良好的可扩展性,所以不必太过于担心它的性能,如果处理速度无法满足我们的要求,可以增加机器来解决。而且,即使消息中间件处理速度有一定的延迟那也是可以接受的,因为前面所介绍的BASE理论就告诉我们了,我们追求的是最终一致性,而非实时一致性,因此消息中间件产生的时延导致事务短暂的不一致是可以接受的。 **对于下游事务的回滚,一般都会采取相应的补偿措施。** 参考文章:[https://blog.csdn.net/u010425776/article/details/79516298][https_blog.csdn.net_u010425776_article_details_79516298] [https://mp.weixin.qq.com/s/9A6ZnpBmAbQYC7kLr1iZCQ][https_mp.weixin.qq.com_s_9A6ZnpBmAbQYC7kLr1iZCQ] [Link 1]: https://www.jianshu.com/p/16b1baf015e8 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70]: /images/20220213/270e676e508e4d9dba17defb6a031eb1.png [20190427085744900.png]: /images/20220213/a5d56efcac4c4831b798765eed096616.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 1]: /images/20220213/050a26338dcd44e3a3f317a7f643881d.png [TCC]: https://www.cnblogs.com/rjzheng/p/10164667.html [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 2]: /images/20220213/752d49c7dda34157b640ede61d7d3966.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 3]: /images/20220213/b4ddeb3b26ce40d6a5eca5faa6c4e3db.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 4]: /images/20220213/afeb247e01c84db283596e4b1a122c30.png [aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ2NZV0w0aWFKTXFpY3l0d0I1U3Q3aWNtUUpaZldhaWI1V2xpY0p1N3FPMVQ2a3l2SlNPYlZpYmliTWU0dFEvNjQwP3d4X2ZtdD1wbmcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ]: /images/20220213/897d11a71b0a410f886f7013d4a9d9bd.png [aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ1c3NDhMdEtQYzJzUzE0S1FwMVFKbXpEZlp1RHI4NEdqWnZabGtEUldXR3g1ekl5b2ljUjRSdWcvNjQwP3d4X2ZtdD1wbmcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ]: /images/20220213/cc3c4252940543d1a3242fbf6b72621e.png [aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ0k2QW5GZFdoRDdqMUs5dVlHelY5N2ljcWNycGxnZ3YxUWNqNXdvdU1KMHRYZkRvanNybUpjNHcvNjQwP3d4X2ZtdD1wbmcmdHA9d2VicCZ3eGZyb209NSZ3eF9sYXp5PTEmd3hfY289MQ]: /images/20220213/145bd90e0a674b1ba9d6aae28da2797d.png [aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9NT3dsTzBJTmZRcDlVOUxVRFNsUUdUOXIzQ09iM25zZ013eEw1eTY3aWJDWGE4TDQ1RmhmZXRvZU5pY1dJczgxbGZFS1daN0VXY0lFODVLNTlwS2dLTUh3LzY0MD93eF9mbXQ9cG5nJnRwPXdlYnAmd3hmcm9tPTUmd3hfbGF6eT0xJnd4X2NvPTE]: /images/20220213/ad2f9154d8f64be8b096ac3584dc46df.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 5]: /images/20220213/a20fade8ac8445b7a9f406479b1563d2.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 6]: /images/20220213/44561520cfea462eae548296d49a6443.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 7]: /images/20220213/63f2e6fda97245c2a6072fce1cfeade7.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 8]: /images/20220213/e24ab7d9ab874d219fcfdda1b79607af.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0ppblhZYW4_size_16_color_FFFFFF_t_70 9]: /images/20220213/8574bd8a9cab4584a55d4a0841781299.png [https_blog.csdn.net_u010425776_article_details_79516298]: https://blog.csdn.net/u010425776/article/details/79516298 [https_mp.weixin.qq.com_s_9A6ZnpBmAbQYC7kLr1iZCQ]: https://mp.weixin.qq.com/s/9A6ZnpBmAbQYC7kLr1iZCQ
相关 分布式-分布式事务 分布式事务 文章目录 分布式事务 一,本地消息表 二,2PC 两阶段提交 三,3PC 三段式提交 四,T 待我称王封你为后i/ 2022年11月27日 15:40/ 0 赞/ 273 阅读
相关 分布式事务01-分布式事务概述 文章目录 1.分布式事务产生的背景 1.1 分布式事务在不同场景下如何产生 1.2 案例 2.事务理论知识 2.1 AC 旧城等待,/ 2022年04月25日 09:54/ 0 赞/ 342 阅读
相关 分布式事务 一、前言 在单个数据库实例时候,我们可以使用一个数据源的事务([本地事务][Link 1] )来保证事务内的多个操作要么全部执行生效,要么全部不生效。在多数据库实例节点时 冷不防/ 2022年04月14日 02:09/ 0 赞/ 360 阅读
相关 分布式事务 现在面试,分布式系统成了标配,而分布式系统带来的分布式事务也成了标配了。因为你做系统肯定要用事务吧,如果是分布式系统,肯定要用分布式事务吧。先不说你搞过没有,起码你得明白有哪几 比眉伴天荒/ 2022年03月15日 11:28/ 0 赞/ 393 阅读
相关 分布式事务 什么是分布式事务 分布式事务涉及到操作多个数据库的事务,分布式事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的`不同节点上`。一个分布式事务可 Dear 丶/ 2022年03月10日 15:27/ 0 赞/ 225 阅读
相关 分布式事务 什么是分布式事务 分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。 简单的说,就是一次大的操作由不同的小操作 Myth丶恋晨/ 2022年02月13日 06:55/ 0 赞/ 307 阅读
相关 分布式事务 1、分布式事务产生的背景 在微服务环境下,因为会根据不同的业务会拆分成不同的服务,比如会员服务、订单服务、商品服务等,让专业的人做专业的事情,每个服务都有自己独立的数据库,并 - 日理万妓/ 2021年10月25日 14:09/ 0 赞/ 425 阅读
相关 分布式事务 阅读: 深入理解分布式事务,高并发下分布式事务的解决方案 [https://blog.csdn.net/qq\_32534441/article/details/890 红太狼/ 2021年10月13日 01:13/ 0 赞/ 455 阅读
相关 分布式事务 在分布式系统中,为了保证数据的高可用,通常,我们会将数据保留多个副本(replica),这些副本会放置在不同的物理的机器上。为了对用户提供正确的 CRUD 等语义,我们需... 灰太狼/ 2020年04月24日 17:42/ 0 赞/ 936 阅读
还没有评论,来说两句吧...