如何基于本地消息表实现分布式事务?

典型回答 本地消息表其实也是借助消息来实现分布式事务的。 这个方案的主要思想是将分布式事务拆分为本地事务和消息事务两个部分,本地事务在本地数据库中进行提交或回滚,而消息事务则将消息写入消息中间件中,以实现消息的可靠投递和顺序性。 一般来说的做法是,在发送消息之前,先创建一条本地消息,并且保证写本地业务数据的操作,和,写本地消息记录的操作在同一个事务中。这样就能确保只要业务操作成功,本地消息一定可以写成功。 然后再基于本地消息,调用MQ发送远程消息。 消息发出去之后,等待消费者消费,在消费者端,接收到消息之后,做业务处理,处理成功后再修改本地消息表的状态。 这个过程中,可能有几个步骤都可能发生失败,那么如果失败了怎么办呢? 1、2如果失败,因为在同一个事务中,所以事务会回滚,3及以后的步骤都不会执行。数据是一致的。 3如果失败,那么就需要有一个定时任务,不断的扫描本地消息数据,对于未成功的消息进行重新投递。 4、5如果失败,则依靠消息的重投机制,不断地重试。 6、7如果失败,那么就相当于两个分布式系统中的业务数据已经一致了,但是本地消息表的状态还是错的。这种情况也可以借助定时任务继续重投消息,让下游幂等消费再重新更改消息状态,或者本系统也可以通过定时任务去查询下游系统的状态,如果已经成功了,则直接推进消息状态即可。 扩展知识 优缺点 优点: 可靠性高:基于本地消息表实现分布式事务,可以将本地消息的持久化和本地业务逻辑操作,放到一个事务中执行进行原子性的提交,从而保证了消息的可靠性。 可扩展性好:基于本地消息表实现分布式事务,可以将消息的发送和本地事务的执行分开处理,从而提高了系统的可扩展性。 适用范围广:基于本地消息表实现分布式事务,可以适用于多种不同的业务场景,可以满足不同业务场景下的需求。 缺点: 实现复杂度高:基于本地消息表实现分布式事务,需要设计复杂的事务协议和消息发送机制,并且需要进行相应的异常处理和补偿操作,因此实现复杂度较高。 系统性能受限:基于本地消息表实现分布式事务,需要将消息写入本地消息表,并且需要定时扫描本地消息表进行消息发送,因此对系统性能有一定影响。 回滚困难:本地消息表的方案,比较适合那种上游成功下游必须成功的场景,比如下单成功了,运费险必须成功。而对于可能需要整个事务都回滚的场景,不适合这个方案,或者说想要做就要很复杂的作回滚机制。 参与方多不适合:虽然消息可以发送给多个监听者,但是本地消息表中的数据只有一条,如果一个事务有多个参与方,这个方案不适合。虽然可以在消息表中增加多个状态字段,表示多个参与者的状态,但是就太耦合了。 **会带来消息堆积扫表慢、集中式扫表会影响正常业务、定时扫表存在延迟问题等问题。**在下文中介绍: ✅本地消息表实现的分布式的缺点有什么? 代码实践 ✅基于本地消息表实现分布式事务保证最终一致性 本地消息表如何设计 ✅为了避免丢消息问题需要落表,如何设计这张消息表? 如何回滚 ✅用了本地消息表的方案,如果下游执行失败了上游如何回滚?

March 22, 2026 · 1 min · santu

如何实现应用中的链路追踪?

典型回答 随着业务量的增长,为了提升整体系统的可用性、性能及可扩展性,很多大型互联网公司都会采用微服务架构,一次业务请求,一般要经过几个微服务调用才能完成,。 一次请求之间要经过很多的系统,那么如何追踪一次请求从头到尾的流程,就至关重要,这样可以帮我们做很好的链路分析,及问题定位。 在业内,有很多链路追踪的工具,如Google的dapper、twitter的zipkin、京东的hydra、大众点评的cat,以及开源的skywalking。建议大家看看Google的这篇论文,可以说它是分布式链路追踪的启蒙之作:https://bigbully.github.io/Dapper-translation/ 不管是哪个实现,在我看来重点就是解决两个问题: 1、生成一个全局的traceId 2、把这个traceId传递下去 生成TraceId 想要追踪一个完成的链路,就需要有一个标识来标记这次调用链,业内把他叫做traceId,一般会在入口处生成一个全局唯一的traceId,比如说在HTTP的请求入口,在定时任务的调度入口等,生成一个traceId。 传递TraceId 在有了一个traceId之后,想要通过它把一次调用过程串联起来,那么就需要所有的系统间调用都得把他传递下去,这里面的调用包括了HTTP请求、RPC请求、MQ消息等,甚至还需要涉及到Redis、MySQL等等可能都需要进行传递。 所以,想要实现一个链路追踪,需要很多中间件一起配合才行,通常这个traceId会存放在RPC的请求头、HTTP的请求头、MQ消息的消息头中进行传递。 系统之间通过这种方式,那系统内部也是需要传递的,所以一般都是用ThreadLocal来实现的,在接收到请求后,会把这个traceId存储在ThreadLocal中,然后就能在当前线程中一直传递下去,并且在记录日志的时候取出来打印到日志中,在需要调远程的时候,取出来传递下去。 但是,如果有多线程怎么办呢?ThreadLocal咋传递呢,这就要用到TTL了: ✅有了InheritableThreadLocal为啥还需要TransmittableThreadLocal? 扩展知识 Span 前面提到了traceId,其实光有traceId还不够,trace帮我们把一次调用串联起来,但是一次调用在每一个系统上都干了什么,干了多久,成功还是失败,这些对我们来说也很重要,这些信息就被记录在span中。 通常一个完整的 Span 具有如下属性: Operation Name:描述了当前接口的行为语义,如具体的哪个接口,哪个URL地址。 SpanId/ParentSpanId:接口调用的层级标识,用于还原 Trace 内部的层次调用关系。 Start/FinishTime:接口调用的开始和结束时间,二者相减就是该次调用的耗时。 StatusCode:响应状态,标识当次调用是成功或失败。 Tags & Events:调用附加信息

March 22, 2026 · 1 min · santu

最大努力通知&事务消息&本地消息表三者区别是什么?

典型回答 最大努力通知、MQ事务消息以及本地消息表都是依赖MQ实现最终一致性的方案。其中本地消息表和MQ事务消息其实是可靠消息最终一致性的两种具体实现。 ✅如何基于MQ实现分布式事务 这三者中,方案最简单的肯定是最大努力通知,因为他不需要保证消费者一定能接收到,只是尽自己最大的努力去通知就行了。最多就是在发消息的地方加一个重试的机制。 但是这个方案缺点也很明显,那就是可能会导致消息的重复和丢失,这个比较容易理解。 虽然最大努力通知也是一种最终一致性的方案,但是他的一致性保障并没有那么强,所以他不适合用在一些一致性要求较高的场景。只适合用在消息丢了也无所谓的场景。比如说下单后邮件通知、开通后发送欢迎短信之类的业务场景。 MQ事务消息以及本地消息表这两个方案一致性要求更高一些,但是同样方案也要更复杂一些。 MQ事务消息的方案首先要求这个MQ是支持事务消息的,其次对业务代码有侵入性,因为本来只需要发一次消息,用了这个方案之后需要改成发送两个half消息,并且同时还得给MQ提供一个反查的接口。 ✅什么是事务消息,为什么需要事务消息? 本地消息表这个方案对代码的侵入性没那么高,并且不需要MQ支持事务消息,但是他需要单独创建一张本地消息表,并且还需要提供一个定时任务来做轮询。所以他的改造成本也不低。 一般来说,事务消息和本地消息表比较适合于对一致性要求没那么高,不要求强一致,但是也不能丢的一些场景,比如用户下单后给用户增加积分。 一般来说,事务消息和本地消息表两个方案是可以互相替换的,用了事务消息的地方都可以换成本地消息表。但是实际来说,如果MQ支持事务消息,那么可以考虑这个方案,如果公司使用的MQ不支持事务消息,那么就可以考虑本地消息表。 或者还有些场景有一些消息持久化、或者对账的需求,那么也建议使用本地消息表的方案。

March 22, 2026 · 1 min · santu

详细介绍下号段模式生成分布式ID的原理和优缺点?

典型回答 号段模式是一种常见的分布式ID生成方案,主要用于生成全局唯一的ID。其核心思想是通过分配一定范围(段)的ID,避免了频繁访问数据库,提高了性能。 号段模式通常通过一个中心服务(比如数据库)来生成ID段(一个区间)。该中心服务负责生成多个ID段,每个ID段具有固定的步长(如1000个、10000个等)。 然后,系统中的各个节点(如微服务、应用服务器等)从该中心服务申请一个号段。 一旦某个节点获取了一个号段,它就可以在这个号段内生成多个ID,而不需要再次与中心服务交互。 号段的范围是预先确定好的,例如:节点A获取到的号段是 [1001, 2000],节点B获取到的号段是 [2001, 3000],每个节点内部的ID是按顺序递增的。 举个具体的例子,假如我的订单表做了分库分表,然后我需要生成一个全局的唯一id,那么我就在一张单表中创建一张表吗sequence表,表中字段如下: id 主键id gmt_create 创建时间 gmt_modified 修改时间 name 表名 value 值 step 步长 假设value的初始值是10000,然后每一次获取号段的步长是1000 这样当第一个台机器想要生成一个订单的时候,他会到sequece表中查询sequence,然后把它的值更新成sequence+1000。然后这台机器会把10000保存在自己的本地缓存中,然后这一次就可以用10000作为订单的主键id了,然后再本地缓存中更新一下10000已经被使用了。 下次再有订单来创建的时候,因为他本地已经缓存了一个号段,那么他就直接用,把10001拿出来用就行了。 这时候如果有另外一台机器也要生成id,他本地缓存还没有值,他就也去sequence表,查询到11000过来了,并且把表中的sequence更新成了12000,这时候他就可以用11000到11999这个号段的id了,在这范围内的订单都不需要再查询数据库了。直接用就行了。 通过这样的方式,既能实现每一次生成的id都不一样,也能减少和数据库的交互次数,提升分布式id的生成效率。同时也能避免完全依靠数据库做自增id带来的数据库单点瓶颈的问题。 但是这个方案还是有缺点的,比如虽然ID在单个节点内部是递增的,但不同节点之间的ID是离散的。比如节点A的ID是1001-2000,节点B的ID是2001-3000,这意味着它们之间的ID并不是连续的。有可能第一秒生成的id是1003,第二秒的是2002,第三秒的是1004。

March 22, 2026 · 1 min · santu

什么是分布式BASE理论?

典型回答 BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)。 BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。 做不到100%可用,那么就做到基本可用。做不到强一致性,那么就做到最终一致性。 想要做到BASE,那么主要就是用这几个手段:中间状态(软状态)+重试(最终一致性)+降级(基本可用) 基本可用(Basically Available) 基本可用是指分布式系统在出现故障的时候,允许损失部分可用性(降级),即保证核心可用。 电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。 ✅什么是服务降级? 软状态( Soft State) 软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。 其实就是中间状态,比如当前INIT,然后处理中推进到PROCESSING,然后可以基于这个PROCESSING不断重试,直到推进到SUCCESS 有了软状态,我们才能重试。 最终一致性( Eventual Consistency) 最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。 扩展知识 ACID和BASE的区别与联系 ACID是传统数据库常用的设计理念,追求强一致性模型。BASE支持的是大型分布式系统,提出通过牺牲强一致性获得高可用性。 ACID和BASE代表了两种截然相反的设计哲学 在分布式系统设计的场景中,系统组件对一致性要求是不同的,因此ACID和BASE又会结合使用。

March 22, 2026 · 1 min · santu

什么是分布式系统的一致性?

典型回答 所谓一致性,是指数据在多个副本之间是否能够保持一致的特性。在聊一致性的时候,其实要搞清楚一致性模型。(概念挺多,但是没办法,这玩意它本身就是理论。想结合代码、示例都做不到,甚至想着画个图都不知道该如何下手) 分布式系统中的一致性模型是一组管理分布式系统行为的规则。它决定了在分布式系统中如何访问和更新数据,以及如何将这些更新提供给客户端。面对网络延迟和局部故障等分布式计算难题,分布式系统的一致性模型对保证系统的一致性和可靠性起着关键作用。在分布式系统中有多种一致性模型可用,每个模型都有其优点和缺点,选择模型取决于系统的具体要求。 大的分类上面,主要有三种,分别是强一致性、弱一致性和最终一致性: 强一致性模型(Strong Consistency): 在强一致性模型下,系统保证每个读操作都将返回最近的写操作的结果,即任何时间点,客户端都将看到相同的数据视图。这包括线性一致性(Linearizability)、顺序一致性(Sequential Consistency)和严格可串行性(Strict Serializability)等子模型。强一致性模型通常牺牲了可用性来实现数据一致性。 弱一致性模型(Weak Consistency): 弱一致性模型放宽了一致性保证,它允许在不同节点之间的数据访问之间存在一定程度的不一致性,以换取更高的性能和可用性。这包括因果一致性(Causal Consistency)、会话一致性(Session Consistency)和单调一致性(Monotonic Consistency)等子模型。弱一致性模型通常更注重可用性,允许一定程度的数据不一致性。 最终一致性模型(Eventual Consistency): 最终一致性模型是一种最大程度放宽了一致性要求的模型。它允许在系统发生分区或网络故障后,经过一段时间,系统将最终达到一致状态。这个模型在某些情况下提供了很高的可用性,但在一段时间内可能会出现数据不一致的情况。 线性一致性 & 顺序一致性 线性一致性(Linearizability)和顺序一致性(Sequential Consistency)是两种强一致性模型。 线性一致性是一种最强的一致性模型,它强调在分布式系统中的任何时间点,读操作都应该返回最近的写操作的结果。 举个例子,如果操作A在操作B之前成功完成,那么操作B在序列化中应该看起来在操作A之后发生,即操作A应该在操作B之前完成。线性一致性强调实时性,确保操作在实际时间上的顺序保持一致。 **顺序一致性也是一种强一致性模型,但相对于线性一致性而言,它放宽了一些限制。**在顺序一致性模型中,系统维护一个全局的操作顺序,以确保每个客户端看到的操作顺序都是一致的。 与线性一致性不同,顺序一致性不强调实时性,只要操作的顺序是一致的,就可以接受一些延迟。 他们的主要区别在于强调**实时性**。线性一致性要求操作在实际时间上的顺序保持一致,而顺序一致性只要求操作的顺序是一致的,但不一定要求操作的实际时间顺序。 顺序一致性 & 最终一致性 很多人看完线性一致性和顺序一致性的区别之后,会容易懵,看上去顺序一致性和我们理解的最终一致性有点像? 那么他们的区别是啥呢? 在时间上,虽然顺序一致性和最终一致性都不强要求实时性,但是最终一致性的时间放的会更宽。并且最终一致性其实并不强调顺序,他只需要保证最终的结果一致就行了,而顺序一致性要求操作顺序必须一致。 并且,顺序一致性还是一种强一致性,比如在Zookeeper中,其实就是通过ZAB算法来保证的顺序一致性,即各个节点之间的写入顺序要求一致。并且要半数以上的节点写入成功才算成功。所以,顺序一致性的典型应用场景就是数据库管理系统以及分布式系统。 而最终一致性通常适用于互联网三高架构的业务开发,如电商网站,社交媒体网站等。

March 22, 2026 · 1 min · santu

什么是拜占庭将军问题

典型回答 拜占庭将军问题是分布式系统中的一个经典问题,由Leslie Lamport等人于1982年提出,是对分布式系统中节点之间进行协调的一种特殊情况的抽象描述。 拜占庭帝国的一支军队要攻打一个城市,攻打的成功需要不同将军协同决策,但是有些将军是不忠诚的,他们可能会发送虚假信息或者故意阻碍其他将军的决策。问题是如何让忠诚的将军在不知道其他将军是否忠诚的情况下做出正确的决策。 **拜占庭将军问题的本质是分布式系统中的协同问题,即如何使得分布式系统中的不同节点能够在相互独立的情况下达成共识。**这个问题对于分布式系统的可靠性和安全性具有重要的意义,同时也是分布式系统研究中的一个重要话题。 解决拜占庭将军问题有许多方法,比较常见的就是通过投票算法、共识算法来解决,但是这些算法其实背后都基于了一个思想,那就是超过半数。 基于多数表决的解决方案:假设总共有N个将军,每个将军发送自己的意见给其他将军,然后将军们根据收到的意见进行投票,如果有超过N/2个将军投票一致,则采取投票的结果。这个方案的前提是假设叛徒的数量不超过总将军数的一半,因为如果超过一半的将军都是叛徒,则无法保证多数投票的结果是正确的。这种解决方案在很多算法中都有实践,如Raft、ZAB、Paxos等。

March 22, 2026 · 1 min · santu

分布式锁有几种实现方式?

典型回答 分布式锁有多种实现方式,比较常见的实现是通过数据库、Redis或者Zookeeper来实现的。 其中数据库的实现可以依赖悲观锁以及数据库表记录来实现,通过Redis的实现可以考虑使用setnx、redission以及redlock实现。使用zk主要是依赖他提供的临时有序节点来实现。 扩展知识 在分析这几种实现方案之前我们先来想一下,我们需要的分布式锁应该是怎么样的? 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行。 这把锁要是一把可重入锁(避免死锁) 这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条) 有高可用的获取锁和释放锁功能 获取锁和释放锁的性能要好 基于数据库实现分布式锁 基于数据库表 要实现分布式锁,最简单的方式可能就是直接创建一张锁表,然后通过操作该表中的数据来实现了。 当我们要锁住某个方法或资源时,我们就在该表中增加一条记录,想要释放锁的时候就删除这条记录。 创建这样一张数据库表: 1 2 3 4 5 6 7 8 CREATE TABLE `methodLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '锁定的方法名', `desc` varchar(1024) NOT NULL DEFAULT '备注信息', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成', PRIMARY KEY (`id`), UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的方法'; 当我们想要锁住某个方法时,执行以下SQL: ...

March 22, 2026 · 1 min · santu

常见的分布式事务有哪些?

典型回答 分布式事务的目的是保证分布式系统中的多个参与方的数据能够保证一致性。即所有参与者,在一次写操作过程中要么都成功,要么都失败。 至于这个一致性到底是怎样的一致性,是强一致性、还是最终一致性,不同的分布式事务方案其实达到的效果并不相同。 ✅什么是分布式系统的一致性? 以下是常见的一些方案和相关介绍。 方案 一致性 介绍 特点 常见中间件 XA(2PC/3PC) 强一致性 https://www.yuque.com/hollis666/ec96i7/du7xnmhttps://www.yuque.com/hollis666/ec96i7/zt707p4ypq68grpf 严格一致,但性能差,容易阻塞资源 Atomikos、Narayana、MySQL XA TCC 最终一致性 https://www.yuque.com/hollis666/ec96i7/xhvbak3ouy6xqiml 业务侵入大、灵活性高、适合高价值场景 Seata TCC、Hmily TCC Saga 最终一致性 异步、非阻塞,适合长事务 Seata Saga、Apache ServiceComb Saga Seata AT 最终一致性 https://www.yuque.com/hollis666/ec96i7/me3ge4vavi0fokgq 自动化高、侵入小,仅支持关系型数据库 Seata AT 本地消息表 最终一致性 https://www.yuque.com/hollis666/ec96i7/xm675quxo1bc5qm8 架构灵活、复杂度中等 RocketMQ、Kafka + 自研方案 事务消息 最终一致性 https://www.yuque.com/hollis666/ec96i7/awrtlggd35yanugp 不阻塞、适合异步场景 RocketMQ 事务消息 最大努力通知 弱一致性 https://www.yuque.com/hollis666/ec96i7/akhq6shbaqc61s5n 简单、高性能、不适合核心业务 RocketMQ普通消息 强一致性方案 如果想要实现强一致性,那么就一定要引入一个协调者,通过协调者来协调所有参与者来进行提交或者回滚。所以,这类方案包含基于XA规范的二阶段及三阶段提交、以及支持2阶段提交。 ✅有了2阶段提交为什么还需要3阶段提交? 最终一致性方案 如果想要实现最终一致性,那么方案上就比较简单,常见的基于可靠消息的最终一致性(本地消息表、事务消息)。 可靠消息最终一致性,顾名思义就是依赖可靠的消息,来实现一种最终一致性的模型。 他的大致流程就是: 1、事务的发起方执行本地事务 2、事务的发起方向事务的参与方发送MQ消息 3、事务的参与方接收到MQ消息后执行自己的本地事务 这里面事务的发起方和参与方都需要各自执行本地事务,他们之间,通过可靠消息来保障最终一致。 那么,怎么样的消息算可靠呢,直接依赖kafka、rocketMQ发送一个消息就可靠了么?显然是不行的,因为我们知道,在出现网络分区、网络延迟等情况时,是没办法保证消息一定可以发出去的,也没办法保证消息发出去就一定能被成功消费。 那么想要做到让这个消息可靠,一般由两种做法,一个是事务消息、一个是本地消息表。 ✅如何基于本地消息表实现分布式事务? ...

March 22, 2026 · 1 min · santu

如何基于MQ实现分布式事务

典型回答 ✅什么是分布式事务? 在分布式事务的实现中,有很多种方案,其中比较常用的就是基于MQ来实现,在MQ的具体实现中,又有很多种具体的方案,从大的方面来说可以分为两种: 可靠消息最终一致性 最大努力通知 可靠消息最终一致性 可靠消息最终一致性,顾名思义就是依赖可靠的消息,来实现一种最终一致性的模型。 他的大致流程就是: 1、事务的发起方执行本地事务 2、事务的发起方向事务的参与方发送MQ消息 3、事务的参与方接收到MQ消息后执行自己的本地事务 这里面事务的发起方和参与方都需要各自执行本地事务,他们之间,通过可靠消息来保障最终一致。 那么,怎么样的消息算可靠呢,直接依赖kafka、rocketMQ发送一个消息就可靠了么?显然是不行的,因为我们知道,在出现网络分区、网络延迟等情况时,是没办法保证消息一定可以发出去的,也没办法保证消息发出去就一定能被成功消费。 那么想要做到让这个消息可靠,一般由两种做法: 1、本地消息表 ✅如何基于本地消息表实现分布式事务? 2、事务消息 ✅什么是事务消息,为什么需要事务消息? ✅RocketMQ的事务消息是如何实现的? 通过这两种方案,都可以保证事务的发起方在执行完本地事务之后,消息一定可以发出去,并且一定能被消费成功。 本地消息表的方案是基于本地事务+重试,来保证MQ消息一定可以发出去。 事务消息的方案是基于MQ的事务消息机制,把一条消息拆成两个half消息,通过2阶段的方式+回调反查来保证消息一定能发出去。 2者都是依赖MQ自身的重试机制+事务参与者反查+对账来保证消息一定可以消费。 最大努力通知 除了可靠消息最终一致性这种以外,还有一种方式就是也使用消息,但是这个消息并不要求一定可靠。这就是最大努力通知。 ✅什么是最大努力通知? ...

March 22, 2026 · 1 min · santu

留言给博主