大家好我是数据库小学妹 上个月接手了一个跨库转账的需求。当时想不就是保证多个数据库操作要么全成功要么全失败嘛能有多难。结果第一个坑就栽在2PC上。用了一个开源框架测试环境跑得好好的一上生产就卡。高峰期响应时间飙到几秒用户投诉了好几轮。排查了半天发现是2PC同步阻塞的问题。后来换成TCC又踩了空回滚的坑账差点对不上。折腾了好几周才算把这事理清楚。今天把这些踩坑经历整理出来希望能帮你少走弯路少踩坑。一致性到底是什么分布式事务要解决的问题多个服务、多个数据库之间怎么让数据保持一致。强一致性任何时刻所有节点的数据都一样。听着挺好但实现成本高性能差。最终一致性允许短暂不一致但最终所有节点会达成一致。成本低性能好。大部分业务不需要强一致性。下单后库存扣减和订单创建中间差几毫秒用户感知不到。但转账这种场景钱不能少不能多就得上更强的保证。2PC和3PC2PC是最经典的分布式事务协议分两个阶段。准备阶段协调者问所有参与者能不能提交参与者执行事务但不提交返回YES或NO。提交阶段所有参与者都返回YES协调者发提交命令否则发回滚命令。这几个问题我在生产环境都遇到过同步阻塞。Prepare阶段所有参与者都锁着资源。我查了监控高峰期锁持有时间达到500ms。业务操作本身才10ms剩下全是等锁。并发一上来请求全排着队响应时间飙到几秒。单点故障。协调者挂了参与者就卡在Prepare阶段。我遇到过一次协调者OOM整个分布式事务卡了十几分钟。当时排查了好久才发现是协调者的问题。脑裂。网络分区时协调者可能给部分参与者发提交部分发回滚。数据就乱了。3PC加了超时机制参与者等太久会自动提交。但还是解决不了脑裂问题。实际项目中很少直接用2PC/3PC更多用TCC或Saga。TCC模式TCC把分布式事务拆成三个阶段Try做资源预留。转账场景先把A账户100块冻结不是直接扣掉。Confirm确认提交。所有Try成功后执行实际操作。把冻结的100块从A转到B。Cancel取消回滚。任一Try失败把冻结的100块解冻。TCC有三个坑我都踩过。空回滚Cancel执行了但Try根本没执行过。我遇到的情况是Try请求超时了调用方以为失败就调Cancel。但Try其实成功了钱已经冻结了。Cancel执行的时候把没冻结的钱给解冻了账对不上。排查时我先以为是网络重试导致的双重扣款。看了半天日志才发现Cancel有记录但Try没有对应的冻结记录。才反应过来——这是空回滚。怎么解Cancel执行前先查Try有没有执行过。加一张事务记录表Try成功就写一条。CREATETABLEtcc_transaction(tx_idVARCHAR(64)PRIMARYKEYCOMMENT事务ID,statusVARCHAR(16)NOTNULLCOMMENTINIT/TRIED/CONFIRMED/CANCELLED,created_atDATETIMEDEFAULTCURRENT_TIMESTAMP);Try执行时先插入记录Cancel执行时先查记录// Try先标记再冻结publicvoidtry(StringtxId){insert(txId,INIT);freezeAmount(100);updateStatus(txId,TRIED);}// Cancel先检查Try是否执行过publicvoidcancel(StringtxId){StringstatusqueryStatus(txId);if(statusnull){// Try没执行过标记取消即可不用解冻insert(txId,CANCELLED);return;}unfreezeAmount(100);updateStatus(txId,CANCELLED);}悬挂Cancel执行完了Try才到。比如网络抖动Cancel先执行资源释放了。然后Try到了资源又被预留了。但没人来Confirm或Cancel资源就一直挂着。怎么解Try执行前检查Cancel有没有执行过。publicvoidtry(StringtxId){StringstatusqueryStatus(txId);if(CANCELLED.equals(status)){return;// Cancel已执行Try不再执行}// 正常执行insert(txId,INIT);freezeAmount(100);updateStatus(txId,TRIED);}幂等Confirm或Cancel可能被重复调用。不处理的话重试时钱可能多扣或多加。怎么解执行前检查状态做过了就跳过。Saga模式另一种方案是Saga。把长事务拆成多个本地事务每个都有对应的补偿操作。下单流程为例创建订单→扣减库存→扣减余额。失败就反向补偿恢复余额→恢复库存→取消订单。Saga分两种。编排式没有中央协调者服务通过事件通信。协调式有中央协调者统一调度。Saga踩坑也不少。最头疼的是补偿风暴。步骤越多补偿链越长。我那个转账场景还好只有两步。后来有个下单流程涉及5个服务中间步骤失败了前面4个都要补偿整个回滚跑了十几秒。还有数据可见性的问题。中间步骤完成后数据已经可见了。用户看到下单成功结果后面扣款失败订单被取消。体验很差。另外补偿操作本身也可能失败需要重试。但重试就要保证幂等不然会出问题。补偿幂等我踩过坑。有一次网络重试库存恢复了两次账对不上。后来每条补偿SQL都加了状态检查-- 补偿前先检查状态UPDATEinventorySETstockstock1,statusCOMPENSATEDWHEREorder_idxxxANDstatus!COMPENSATED;TCC和Saga怎么选我现在的理解维度TCCSaga开发成本高每个操作都要写Try/Confirm/Cancel中每个操作写正向补偿资源锁Try阶段锁定时间短不锁定中间数据可见一致性最终一致通过Confirm/Cancel保证最终一致通过补偿保证适用场景转账、支付等强一致要求订单、物流等长流程踩坑重点空回滚、悬挂补偿风暴、数据可见性实际项目中很多场景用消息队列加本地事务表就够了。能用简单方案解决的别上复杂框架。避坑清单别一上来就上分布式事务。先想想能不能用本地事务解决大部分场景不需要。TCC一定要处理空回滚、悬挂、幂等。这三个坑我都踩过不处理账迟早对不上。Saga补偿操作必须加幂等检查。我之前没加网络重试导致库存恢复了两次。框架选型要慎重。AT模式看着简单但锁竞争激烈高并发场景扛不住。监控必须到位。分布式事务出了问题排查很麻烦建议用链路追踪把调用链记下来。降级方案提前准备好。我有一次Cancel操作卡了第三方接口慢整个事务卡了20分钟才发现。后来加了超时控制和降级方案才没再出问题。你们项目里用的什么分布式事务方案评论区聊聊有踩过坑的也欢迎分享。我是数据库小学妹咱们下篇见