news 2026/10/16 5:04:57

分布式事务七种实现方式详解:从2PC到最大努力通知的选型与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分布式事务七种实现方式详解:从2PC到最大努力通知的选型与避坑指南

1. 分布式事务的七种实现方式:从理论到落地的完整拆解

分布式事务这个话题,但凡做过微服务或者分库分表的同学,应该都不陌生。我第一次接触它是在一个订单系统重构的项目里,当时把单体拆成了订单、库存、支付三个服务,结果上线第一天就出了数据不一致的问题——订单创建成功了,库存扣减失败,用户钱也付了,但货发不出去。那会儿才真正意识到,分布式事务不是“要不要做”的问题,而是“怎么做才不会把自己坑死”的问题。

这篇文章我打算把分布式事务的七种主流实现方式从头到尾捋一遍,包括它们各自的原理、适用场景、实操要点,以及我在实际项目中踩过的坑。不管你是刚接触分布式系统的新手,还是正在做技术选型的老手,应该都能从中找到对自己有用的东西。七种方式分别是:两阶段提交(2PC)、三阶段提交(3PC)、本地消息表、事务消息、TCC补偿事务、Saga模式、以及最大努力通知。每一种我都会给出核心思路、关键实现细节和实际落地时的注意事项。

2. 两阶段提交(2PC):最经典但也最容易被误用

2.1 核心原理与执行流程

两阶段提交是分布式事务里最经典的方案,它的思路非常直接:引入一个协调者(Coordinator),把所有参与者(Participant)分成两个阶段来管理。第一阶段叫“准备阶段”,协调者问所有参与者“你们能不能提交”,参与者执行本地事务但不提交,把资源锁住,然后回复“可以”或者“不行”。第二阶段叫“提交阶段”,如果所有参与者都回复“可以”,协调者就通知大家提交;只要有任何一个回复“不行”,协调者就通知所有人回滚。

这个流程听起来很简单,但真正落地的时候,问题往往出在细节上。比如准备阶段参与者执行了本地事务但没提交,这时候资源是被锁住的,如果协调者在第二阶段挂了,参与者就会一直锁着资源,直到超时。这个超时时间设多长?设短了可能误杀正常事务,设长了系统吞吐量直接崩掉。我在一个金融项目里见过把超时设成30分钟的,结果高峰期数据库连接池直接被占满,整个系统雪崩。

2.2 实操中的关键参数与避坑要点

如果你决定用2PC,有几个参数必须仔细调。第一个是事务超时时间,这个值要根据你业务的最长执行时间来定,一般建议比P99耗时多出50%左右。第二个是协调者重试次数,协调者通知参与者提交或回滚时,如果网络抖动导致失败,需要重试,但重试次数不能无限,一般3到5次就够了,再多说明网络本身有问题。第三个是参与者锁等待超时,这个要和数据库的锁等待参数配合调,避免数据库层面先超时导致状态不一致。

注意:2PC最大的问题是同步阻塞。参与者在准备阶段锁住资源后,必须等到协调者的最终指令才能释放。如果协调者宕机,所有参与者都会阻塞。所以2PC不适合高并发场景,更适合内部管理系统这类并发量不大但一致性要求极高的业务。

还有一个坑是协调者的单点问题。协调者本身如果挂了,整个事务就卡住了。常见的做法是把协调者做成集群,用选举机制选主,但这又引入了新的复杂度。我在实际项目中更倾向于用现成的框架,比如Seata的AT模式,它本质上也是2PC的变种,但把协调者的高可用和重试机制都封装好了,省去了很多自己造轮子的风险。

2.3 适用场景与替代方案对比

2PC最适合的场景是参与者数量少、执行时间短、并发量低的业务。比如跨两个数据库的转账操作,或者内部ERP系统里的多模块数据同步。一旦参与者超过5个,或者单个参与者的执行时间超过1秒,2PC的阻塞问题就会变得非常明显。

和后面要讲的TCC相比,2PC的优势是业务侵入性低,不需要每个参与者都实现补偿逻辑;劣势是性能差、协调者压力大。和本地消息表相比,2PC的实时性更好,但可用性更差。所以选型的时候,如果业务能接受最终一致性,我一般不建议用2PC;如果必须强一致,那2PC或者它的变种(比如Seata AT)是绕不开的选择。

3. 三阶段提交(3PC):给2PC加一层保险

3.1 相比2PC的改进点

3PC是在2PC基础上改进的,核心变化是加了CanCommit阶段,把原来的两阶段变成了三阶段:CanCommit、PreCommit、DoCommit。CanCommit阶段协调者只是问参与者“你们现在状态正常吗”,参与者不执行任何事务操作,只检查自身资源是否可用。如果所有参与者都回复正常,才进入PreCommit阶段,这时候才真正执行本地事务但不提交。最后DoCommit阶段才是真正的提交或回滚。

这个改进的目的是减少阻塞范围。在2PC里,参与者在准备阶段就锁资源了,一旦协调者挂了就全阻塞。而在3PC里,CanCommit阶段不锁资源,只有确认所有参与者都健康后才进入PreCommit,这样协调者挂掉的影响面会小一些。另外3PC还引入了超时自动提交机制:如果参与者在PreCommit之后等待DoCommit指令超时,它会默认提交而不是一直等。这个设计是为了避免协调者宕机导致参与者永久阻塞。

3.2 为什么实际项目里很少见到3PC

虽然3PC在理论上比2PC更安全,但实际项目中我几乎没见过纯3PC的落地案例。原因有几个:第一,多了一次网络交互,性能比2PC更差;第二,超时自动提交有风险,如果网络分区导致参与者没收到回滚指令,它会误提交,反而造成数据不一致;第三,实现复杂度高,协调者和参与者都要维护更多的状态机,出bug的概率更大。

我在一个跨机房的数据同步项目里尝试过3PC,结果发现跨机房的网络延迟让CanCommit阶段的耗时比预期多了三倍,整体吞吐量还不如直接用2PC加超时重试。后来我们改成了本地消息表方案,虽然一致性变成了最终一致,但可用性和性能都上了一个台阶。

提示:3PC更适合作为理解分布式事务演进过程的中间形态,实际选型时优先考虑2PC的成熟框架或者最终一致性方案。如果非要用3PC,建议只在同机房、低延迟、参与者数量极少的场景下尝试。

3.3 3PC的状态机与异常处理

3PC的状态机比2PC复杂不少。协调者有四个状态:初始、CanCommit等待、PreCommit等待、DoCommit等待。参与者有五个状态:初始、CanCommit响应、PreCommit执行、DoCommit执行、完成。每个状态转换都要处理超时和网络异常,代码量至少是2PC的两倍。

异常处理的关键在于超时策略的差异化。CanCommit阶段超时,协调者直接中止事务;PreCommit阶段超时,协调者需要重试或者中止;DoCommit阶段超时,参与者默认提交。这个默认提交的逻辑是3PC最大的争议点,因为如果协调者实际上是想回滚的,但网络断了导致参与者没收到回滚指令,参与者就会错误地提交。所以3PC在网络分区场景下反而可能比2PC更不安全。

4. 本地消息表:最终一致性的务实之选

4.1 核心思路与表结构设计

本地消息表是我在实际项目里用得最多的方案,没有之一。它的核心思路特别朴素:把分布式事务拆成两个本地事务,中间用一张消息表来衔接。具体来说,业务服务在执行本地业务操作时,同时往本地消息表里插一条记录,这两个操作在同一个本地事务里完成。然后有一个后台任务不断扫描消息表,把未发送的消息投递到消息队列,投递成功后就标记为已发送。下游服务从消息队列消费消息,执行自己的业务逻辑。

消息表的设计有几个关键字段:消息ID(全局唯一)、业务类型、消息内容(JSON格式)、状态(待发送、已发送、已消费、失败)、重试次数、创建时间、更新时间。其中消息ID必须全局唯一,可以用雪花算法生成,也可以用业务主键加时间戳。状态字段要能区分“投递成功”和“消费成功”,因为投递成功不代表下游一定处理成功了。

CREATE TABLE local_message ( id BIGINT PRIMARY KEY, message_id VARCHAR(64) NOT NULL UNIQUE, biz_type VARCHAR(32) NOT NULL, payload TEXT NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT '0待发送 1已发送 2已消费 3失败', retry_count INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_status_created (status, created_at) );

4.2 消息投递与消费的可靠性保障

投递环节的可靠性靠定时任务加乐观锁来保证。定时任务每隔几秒扫描一次状态为“待发送”的消息,用SELECT ... FOR UPDATE或者版本号乐观锁来避免多实例重复投递。投递到消息队列后,把状态改成“已发送”。如果投递失败,重试次数加一,超过阈值就标记为“失败”并告警。

消费环节的可靠性靠幂等处理来保证。因为消息队列可能重复投递,下游服务必须能识别重复消息。常见的做法是用消息ID做去重表,消费前先查一下这个ID有没有处理过,处理过就直接返回成功。去重表的清理策略也很重要,不能无限增长,一般保留最近7天或者30天的记录就够了。

注意:本地消息表方案里,消息表和业务表必须在同一个数据库实例里,否则本地事务就失效了。如果业务已经分库分表,消息表要跟着业务表走,不能单独放一个库。

我在一个电商项目里用这个方案处理订单和积分的同步,日均消息量大概200万条。定时任务的扫描间隔设的是3秒,每次批量拉取500条,投递成功率99.99%以上。唯一一次出问题是有台机器时钟漂移,导致乐观锁版本号冲突,后来加了NTP同步就再没出现过。

4.3 消息积压与重试策略的实战调优

消息积压是本地消息表最常见的运维问题。积压的原因通常有三个:下游消费能力不足、消息队列故障、定时任务执行太慢。排查的时候先看消息表里“待发送”和“已发送”的数量趋势,如果“待发送”持续增长,说明投递环节有问题;如果“已发送”增长但“已消费”不增长,说明下游消费有问题。

重试策略要分场景设计。网络超时这类临时故障,可以用指数退避重试,第一次等1秒,第二次等2秒,第三次等4秒,最多重试5次。业务逻辑错误这类永久故障,重试再多次也没用,应该直接标记失败并告警,人工介入处理。我在项目里把重试次数和重试间隔都做成了可配置的,不同业务类型用不同的策略,比如支付相关的重试次数多一些,日志相关的重试几次就放弃。

5. 事务消息:消息队列原生的分布式事务支持

5.1 半消息机制与回查逻辑

事务消息是消息队列(比如RocketMQ)提供的一种原生分布式事务支持。它的核心是半消息机制:生产者先往消息队列发一条半消息,这条消息对消费者不可见;然后生产者执行本地事务;根据本地事务的结果,再向消息队列发送提交或回滚指令。如果生产者发送提交/回滚指令失败,消息队列会回查生产者的本地事务状态,根据回查结果决定消息是投递还是丢弃。

这个机制比本地消息表优雅的地方在于,消息的可靠性由消息队列保证,不需要自己维护消息表和定时任务。但代价是强依赖消息队列的可用性,如果消息队列挂了,整个事务就卡住了。另外回查逻辑需要生产者实现一个回查接口,消息队列会定期调用这个接口来确认事务状态。

// 生产者发送事务消息的伪代码 TransactionMQProducer producer = new TransactionMQProducer("group"); producer.setTransactionListener(new TransactionListener() { @Override public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { // 执行本地事务 boolean success = doLocalBusiness(msg); return success ? LocalTransactionState.COMMIT_MESSAGE : LocalTransactionState.ROLLBACK_MESSAGE; } @Override public LocalTransactionState checkLocalTransaction(MessageExt msg) { // 回查本地事务状态 return queryLocalTransactionStatus(msg.getTransactionId()); } }); producer.sendMessageInTransaction(msg, null);

5.2 回查接口的设计要点与常见陷阱

回查接口的设计有几个坑。第一,回查接口必须幂等,因为消息队列可能多次回查同一条消息。第二,回查接口不能太慢,一般要求100毫秒内返回,否则消息队列会认为回查失败并继续重试。第三,回查结果要准确,如果本地事务实际上成功了但回查返回“未知”,消息队列会一直回查,直到超过最大回查次数(默认15次)后丢弃消息,导致数据不一致。

我在一个支付项目里用事务消息处理支付和订单的状态同步,回查接口一开始没做缓存,每次回查都查数据库,结果高峰期数据库压力很大。后来加了一层本地缓存,把最近5分钟的事务状态缓存在内存里,回查命中率90%以上,数据库压力直接降了一个数量级。

提示:事务消息最适合本地事务执行时间短、消息队列可靠的场景。如果本地事务要跑好几秒,半消息会一直占着消息队列的资源,影响其他消息的吞吐。另外事务消息只保证“至少一次”投递,消费端还是要做幂等。

5.3 事务消息与本地消息表的选型对比

事务消息和本地消息表解决的是同一类问题,但适用场景不同。事务消息的优势是代码简洁,不需要自己维护消息表和定时任务;劣势是强依赖消息队列,而且回查逻辑增加了生产者的复杂度。本地消息表的优势是不依赖特定消息队列,可以用任何MQ甚至HTTP接口来投递;劣势是需要自己保证消息表的可靠性和定时任务的稳定性。

选型的时候我一般看两个因素:如果团队已经在用RocketMQ这类支持事务消息的队列,而且业务对消息队列的可用性有信心,那就用事务消息;如果消息队列是多种混用,或者业务对消息队列的依赖需要降到最低,那就用本地消息表。两者也可以结合使用,比如核心业务用事务消息,非核心业务用本地消息表。

6. TCC补偿事务:高性能场景下的精细控制

6.1 Try-Confirm-Cancel三阶段拆解

TCC是Try-Confirm-Cancel的缩写,它把每个参与者的操作拆成三个方法:Try阶段做资源检查和预留,Confirm阶段做真正的提交,Cancel阶段做回滚。和2PC最大的区别是,TCC的资源锁定是在业务层面实现的,不依赖数据库的锁,所以性能更好,但业务侵入性也更强。

举个例子,库存扣减的TCC实现:Try阶段先检查库存是否充足,如果充足就冻结一部分库存(比如把可用库存改成冻结库存);Confirm阶段把冻结库存真正扣掉;Cancel阶段把冻结库存释放回可用库存。这三个方法都要保证幂等,因为网络重试可能导致重复调用。

public interface InventoryTccService { // Try:检查并冻结库存 boolean tryFreeze(String orderId, String skuId, int quantity); // Confirm:确认扣减 boolean confirmFreeze(String orderId, String skuId, int quantity); // Cancel:释放冻结 boolean cancelFreeze(String orderId, String skuId, int quantity); }

6.2 空回滚、悬挂与幂等三大难题的解决

TCC有三个经典难题:空回滚、悬挂、幂等。空回滚是指Try方法还没执行,Cancel方法就被调用了(比如网络超时导致协调者以为Try失败了)。解决方法是Cancel方法里先查一下Try有没有执行过,没执行过就直接返回成功。悬挂是指Cancel比Try先执行,然后Try才到,导致资源被冻结但没人释放。解决方法是在Try方法里检查是否有对应的Cancel记录,如果有就拒绝执行。

幂等相对好解决,每个TCC操作都带一个全局事务ID,用去重表或者状态机来保证同一个事务ID的同一个操作只执行一次。我在项目里用Redis存事务状态,Try、Confirm、Cancel各自有独立的状态标记,每次操作前先查状态,已经执行过的直接返回。

注意:TCC的三大难题必须在设计阶段就考虑清楚,不能等到上线后才发现。空回滚和悬挂的解决方案需要在Try和Cancel方法里都加校验逻辑,幂等需要全局事务ID和状态存储。这些额外的逻辑会让代码量增加不少,但这是TCC的必经之路。

6.3 TCC框架选型与业务改造量评估

TCC框架我用过两个:一个是Seata的TCC模式,一个是Hmily。Seata的TCC模式需要定义三个接口方法,用注解标记,框架负责协调和重试。Hmily更轻量,但功能也少一些。选型的时候主要看协调者的高可用能力和对业务代码的侵入程度。

TCC的业务改造量是七种方案里最大的。每个参与者都要实现三个方法,还要处理空回滚、悬挂、幂等,代码量至少是原来的三倍。所以TCC只适合核心业务、高并发、强一致的场景,比如支付、库存、优惠券。非核心业务用TCC就是杀鸡用牛刀,维护成本太高。

7. Saga模式:长事务的拆分与补偿

7.1 事件驱动与命令驱动的差异

Saga模式的核心思想是把长事务拆成多个本地短事务,每个短事务都有对应的补偿操作。如果某个短事务失败了,就依次调用前面已经成功的短事务的补偿操作,把数据回滚到初始状态。Saga有两种实现方式:事件驱动和命令驱动。事件驱动是每个服务执行完本地事务后发一个事件,下一个服务监听事件并执行;命令驱动是有一个协调者依次调用每个服务的正向和补偿接口。

事件驱动的优势是松耦合,服务之间不需要知道彼此的存在;劣势是流程不直观,排查问题的时候要追事件链。命令驱动的优势是流程清晰,协调者知道每一步的执行状态;劣势是协调者可能成为瓶颈。我在项目里更倾向于命令驱动,因为排查问题方便,而且协调者可以做高可用。

7.2 补偿操作的幂等与重试设计

Saga的补偿操作必须幂等,因为补偿可能被重试多次。另外补偿操作要可重试,如果补偿失败(比如网络问题),要能自动重试。重试策略和本地消息表类似,指数退避加最大次数限制。如果补偿一直失败,就需要人工介入,所以Saga模式必须配套告警和人工干预流程。

还有一个坑是补偿的顺序。如果Saga有5个步骤,第3步失败了,补偿要按2、1的顺序逆序执行。如果补偿过程中第2步的补偿又失败了,那第1步的补偿还要不要执行?我的做法是继续执行,因为不执行的话数据会更不一致。同时记录补偿失败的状态,触发告警。

7.3 Saga与TCC的适用边界

Saga和TCC经常被拿来比较。TCC是预留资源,Confirm阶段才真正提交,所以一致性更强;Saga是直接提交,失败后靠补偿回滚,所以一致性更弱。TCC适合短事务、强一致的场景,Saga适合长事务、最终一致的场景。

举个例子,旅游预订系统里,订机票、订酒店、租车是一个长流程,可能持续几分钟甚至几小时。这种场景用TCC就不合适,因为资源锁定时间太长;用Saga就很合适,每个步骤都是本地事务,失败后补偿。反过来,支付扣款这种短事务,用TCC更合适,因为需要强一致。

8. 最大努力通知:最弱但最实用的兜底方案

8.1 通知机制与重试策略

最大努力通知是七种方案里一致性最弱的,但也是最简单、最实用的。它的核心是发起方尽最大努力把结果通知给接收方,如果通知失败就重试,重试到一定次数后就不再重试,由接收方主动来查询。这个方案不保证强一致,也不保证最终一致,只保证“尽力而为”。

通知机制通常用HTTP回调或者消息队列。HTTP回调的实时性好,但可靠性差;消息队列的可靠性好,但实时性差。我在项目里一般用消息队列做通知,接收方消费消息后处理业务,处理成功就ACK,失败就重试。重试次数用完后,消息进入死信队列,由人工处理。

8.2 对账补偿与人工干预流程

最大努力通知必须配套对账系统。因为通知可能丢失,接收方需要定期和发起方对账,找出不一致的数据并补偿。对账的频率看业务要求,支付类业务一般T+1对账,非核心业务可以每周对账。对账的维度可以是订单号、流水号、时间范围等。

人工干预流程也很重要。对账发现不一致后,不能自动补偿,因为自动补偿可能引入新的不一致。我的做法是生成差异工单,由运营人员确认后再手动触发补偿。补偿操作要记录操作日志,方便追溯。

提示:最大努力通知适合对一致性要求不高、能接受人工介入的场景,比如积分发放、优惠券推送、消息通知。核心交易链路不要用这个方案,否则对账和人工干预的成本会很高。

8.3 什么场景下“最弱”反而是最优解

最大努力通知虽然一致性最弱,但在某些场景下反而是最优解。比如营销活动的中奖通知,用户中奖后发短信通知,短信可能发送失败,但用户下次打开APP还能看到中奖记录,所以短信通知失败影响不大。再比如日志收集,日志丢几条对业务没影响,用最大努力通知就够了。

选型的时候不要一味追求强一致,要根据业务的实际需求来。强一致意味着更高的成本和更低的性能,如果业务能接受最终一致甚至“尽力而为”,那就选最简单的方案。我在项目里经常看到有人用TCC做日志同步,这就是典型的过度设计。

9. 七种方案选型对照与实战建议

9.1 一致性、性能、成本三维对比

方案一致性性能业务侵入性运维成本适用场景
2PC强一致低低中短事务、低并发
3PC强一致更低低高理论参考,实际少用
本地消息表最终一致高中中通用场景
事务消息最终一致高低低已用RocketMQ的场景
TCC强一致高高高核心业务、高并发
Saga最终一致高中中长事务、多步骤
最大努力通知弱一致最高低低非核心、可人工介入

这张表是我根据多个项目的实际经验总结的,不一定绝对准确,但大方向可以参考。选型的时候先看一致性要求,再看性能和成本,最后看团队的技术栈和维护能力。

9.2 按业务场景的选型决策树

如果业务是支付、库存扣减这类核心链路,要求强一致,并发量又高,那就选TCC。如果团队不想写太多补偿逻辑,可以用Seata的AT模式,它本质上是2PC的优化版,业务侵入性低一些。

如果业务是订单和积分的同步这类非核心链路,能接受最终一致,那就选本地消息表或者事务消息。如果已经在用RocketMQ,优先用事务消息;如果消息队列不统一,用本地消息表。

如果业务是旅游预订、保险理赔这类长流程,步骤多、执行时间长,那就选Saga。如果业务是短信通知、日志收集这类对一致性要求极低的场景,用最大努力通知就够了。

9.3 混合使用与渐进式演进策略

实际项目里很少只用一种方案,通常是混合使用。比如核心支付链路用TCC,订单和积分的同步用本地消息表,营销通知用最大努力通知。不同链路用不同方案,既保证了核心业务的一致性,又控制了整体成本。

渐进式演进也很重要。不要一开始就上最复杂的方案,可以先从本地消息表开始,等业务量上来了、一致性要求提高了,再逐步替换成TCC或者事务消息。我在项目里见过一上来就用TCC的,结果代码复杂度太高,团队维护不过来,最后又退回了本地消息表。

注意:分布式事务没有银弹,每种方案都有取舍。选型的核心是匹配业务需求,而不是追求技术上的“先进”。能用简单方案解决的,就不要用复杂方案。

10. 分布式事务落地中的通用避坑经验

10.1 幂等设计是所有方案的基础

不管用哪种分布式事务方案,幂等都是必须的。网络重试、消息重复投递、用户重复点击,都会导致同一个操作被执行多次。幂等的实现方式有几种:用全局唯一ID做去重表、用状态机做状态校验、用数据库唯一索引做冲突检测。我一般用全局事务ID加去重表,简单可靠。

去重表的设计要注意清理策略,不能无限增长。可以按时间分区,定期删除旧数据;也可以用Redis做去重,设置合理的过期时间。Redis去重的优势是性能好,劣势是可靠性不如数据库,重启可能丢数据。核心业务建议用数据库去重,非核心业务可以用Redis。

10.2 超时与重试的参数调优

超时和重试的参数没有万能值,必须根据业务特点来调。超时时间一般设为业务P99耗时的1.5到2倍,太短会导致正常请求被误判为超时,太长会导致故障时恢复慢。重试次数一般3到5次,重试间隔用指数退避,避免重试风暴。

还有一个容易被忽略的参数是最大等待时间。比如TCC的Confirm阶段,如果协调者一直不发起Confirm,参与者的资源会一直冻结。所以参与者要设置一个最大等待时间,超过后就自动回滚或者告警。这个时间一般设为业务超时时间的2到3倍。

10.3 监控告警与人工兜底机制

分布式事务的监控比单机事务重要得多。需要监控的指标包括:事务成功率、平均耗时、重试次数、补偿次数、积压消息数。这些指标要接入告警系统,超过阈值就通知值班人员。

人工兜底机制是最后一道防线。不管系统设计得多好,总会有意料之外的情况。所以必须有一个人工干预后台,能查询事务状态、手动触发补偿、查看操作日志。这个后台不需要很复杂,但必须有,否则出了问题只能干瞪眼。

我在项目里还加了一个事务日志表,记录每个分布式事务的完整生命周期:开始时间、各阶段耗时、最终状态、补偿记录。这个表在排查问题时特别有用,能快速定位是哪个环节出了问题。日志表的数据量会比较大,可以按时间分区,保留最近3个月的数据。

最后分享一个小技巧:在测试环境模拟各种异常。网络超时、消息队列宕机、数据库连接断开、协调者重启,这些异常在测试环境都要覆盖到。我见过很多项目在测试环境一切正常,上线后一遇到网络抖动就数据不一致,就是因为异常场景没测到位。分布式事务的测试不能只测正常流程,异常流程才是重点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 5:04:25

视觉中间件 VM PRO 2.7:从图像接入到推理调度的视觉开发框架实践指南

视觉框架 VM PRO 2.7 是我这几个月在好几个视觉项目里反复用下来的一套工具集。它本身不是某个具体的检测算法,也不是一个渲染引擎,而是一套把图像接入、预处理、推理调度、结果后处理、设备适配揉在一起的视觉中间件,行话叫视觉开发框架也行…

作者头像 李华
网站建设 2026/10/16 5:04:10

从零训练MiniMind:单显卡跑通大模型训练全流程

很多人提到大模型训练,第一反应就是“没有几十张卡根本搞不了”。但实际上,一个参数量只有四五十M 的 MiniMind 也能完整走一遍从零训练的流程,而且单张消费级显卡就能跑完。这一篇是这个学习笔记系列的第 10 篇,前面的内容已经把…

作者头像 李华
网站建设 2026/10/16 5:04:09

大厂开源AI工作助理WorkPilot:智能体架构与自动化实践

国内某头部大厂最近又扔出一个王炸——直接把内部打磨已久的 AI 工作助理开源了。消息一出来,开发者社区立刻炸了锅。我第一时间把源码拉下来跑了一遍,结论是:这玩意确实比市面上大多数"对话机器人"实用得多,不是那种只…

作者头像 李华
网站建设 2026/10/16 5:04:09

基于TMS320F28335的FOC电机控制实战:从PWM发波到电流环整定

简介:针对TI TMS320F28335 DSP平台整理的一套电机磁场定向控制(FOC)与空间矢量调制(SVPWM)实现方案,适合从事电机驱动、工业自动化、嵌入式电力电子开发的工程师及高校相关专业学生参考。资源围绕三相电流采…

作者头像 李华
网站建设 2026/10/16 5:03:37

Redis使用第一弹:核心数据结构与缓存穿透、Spring Boot集成指南

大概五六年前我刚开始接触Redis的时候,真没觉得它有多少学问。那会儿项目里最常用的就是set、get两个命令,塞在接口里当缓存用,线上数据量也不大,感觉它就是一把"高级点的Map"。真正意识到Redis不是这么用的&#xff0c…

作者头像 李华
网站建设 2026/10/16 5:03:35

Apollo-11:模块化个人知识管理与自动化工作流实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华