分布式事务的7种方案,yyds!

大家好,我是苏三,又跟大家见面了。

前言

分布式事务问题,无论在面试,还是工作中经常会遇到。

分布式系统下,数据一致性不再是数据库事务那么简单的。

分布式事务作为其中最复杂的挑战之一,曾让无数团队深夜加班、焦头烂额。

今天这篇文章就跟大家一起聊聊分布式事务问题的7种常见解决方案,希望对你会有所帮助。

picture.image

1.为什么分布式事务如此棘手?

在单体应用时代,数据库的ACID事务保证了数据一致性。

但在微服务架构下,一个业务操作需要跨多个服务、多个数据库 ,传统事务模型不再适用。

想象一下电商下单场景:

  1. 订单服务创建订单(订单数据库)
  2. 库存服务扣减库存(库存数据库)
  3. 支付服务处理支付(支付数据库)
  4. 积分服务增加积分(积分数据库)

这四个操作要么全部成功,要么全部失败

这就是分布式事务要解决的核心问题。

那么,如何解决问题呢?

最近建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了不少的工作内推岗位。

扫码加苏三的微信:li_su223,备注:所在城市,即可进群。

picture.image

  1. 常见的解决方案

2.1 2PC(两阶段提交)

该方案是强一致性方案。

2PC是最经典的分布式事务协议,通过协调者(Coordinator) 统一调度参与者(Participant) 的执行。

分为两个阶段:

picture.image

第一阶段:准备阶段 协调者询问所有参与者:“能否提交事务?”

参与者执行本地事务但不提交,锁定资源并回复YES/NO。

  
// 参与者伪代码  
public boolean prepare() {  
    try {  
        startTransaction();  
        executeSql("UPDATE account SET frozen = 100 WHERE id = 1"); // 预留资源  
        return true; // 返回YES  
    } catch (Exception e) {  
        rollback();  
        return false; // 返回NO  
    }  
}  

第二阶段:提交/回滚阶段

  • 若所有参与者返回YES,协调者发送commit命令,参与者提交事务
  • 若有任一参与者返回NO,协调者发送rollback命令,参与者回滚事务

致命缺陷

  • 同步阻塞 :所有参与者在prepare后锁定资源,直到收到commit/rollback(高并发下吞吐量骤降)
  • 单点故障 :协调者宕机导致参与者永久阻塞
  • 数据不一致 :网络分区时部分参与者可能提交成功

2.2 3PC(三阶段提交)

该方案也是强一致性方案。

3PC可以解决2PC阻塞问题。

3PC在2PC基础上增加预提交阶段 ,并引入超时机制

picture.image

  1. CanCommit阶段 :协调者询问参与者状态(不锁定资源)
  2. PreCommit阶段 :参与者锁定资源并执行SQL(不提交)
  3. DoCommit阶段 :正式提交

改进点

  • 参与者超时未收到命令自动提交(降低阻塞风险)
  • 预提交阶段发现异常可提前终止

但依然存在问题

  • 网络分区时仍可能数据不一致
  • 实现复杂度显著增加

2.3 TCC(Try-Confirm-Cancel)

该方案是最终一致性方案。

它是业务层面的2PC。

TCC将业务逻辑拆分为三个阶段:

  • Try :预留资源(如冻结库存)
  • Confirm :确认操作(正式扣减库存)
  • Cancel :释放资源(解冻库存)
  
// 积分服务TCC实现  
publicclass PointsService {  
      
    @Transactional  
    public boolean tryDeductPoints(Long userId, int points) {  
        // 检查用户积分是否充足  
        UserPoints user = userPointsDao.selectForUpdate(userId);  
        if (user.getAvailable() < points) {  
            thrownew InsufficientPointsException();  
        }  
        // 冻结积分  
        userPointsDao.freeze(userId, points);  
    }  
      
    public boolean confirmDeductPoints(Long userId, int points) {  
        // 实际扣减冻结积分  
        userPointsDao.confirmDeduct(userId, points);  
    }  
      
    public boolean cancelDeductPoints(Long userId, int points) {  
        // 释放冻结积分  
        userPointsDao.unfreeze(userId, points);  
    }  
}  

执行流程

  1. 主业务调用所有服务的try方法
  2. 全部try成功则调用confirm;任一try失败则调用cancel

优势

  • 无全局锁 :只在try阶段锁定局部资源
  • 高可用 :协调者可集群部署

挑战

  • 手动实现回滚逻辑 (业务侵入性强)
  • 所有服务需提供三种接口

金融核心系统首选:某银行跨境支付系统采用TCC方案,日均处理200万笔交易,跨5个服务的事务成功率99.99%

2.4 可靠消息最终一致性

该方案也是最终一致性方案。

可以使用RocketMQ的事务消息。

RocketMQ的事务消息完美解决本地操作与消息发送的一致性 问题:

picture.image

关键步骤

  1. 发送half消息(对消费者不可见)
  2. 执行本地事务
  3. 根据本地事务结果commit/rollback
  4. MQ定时回查未决事务

示例代码

  
// 订单服务使用事务消息  
publicclass OrderService {  
      
    @Autowired  
    private RocketMQTemplate rocketMQTemplate;  
      
    public void createOrder(Order order) {  
        // 1. 发送half消息  
        Message msg = MessageBuilder.withPayload(order).build();  
        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(  
            "order\_topic", msg, null);  
          
        // 2. 执行本地事务(在TransactionListener中实现)  
    }  
}  
  
// 事务监听器  
@RocketMQTransactionListener  
class OrderTransactionListener implements RocketMQLocalTransactionListener {  
  
    @Override  
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {  
        try {  
            Order order = (Order) msg.getPayload();  
            orderDao.save(order); // 本地事务  
            return RocketMQLocalTransactionState.COMMIT;  
        } catch (Exception e) {  
            return RocketMQLocalTransactionState.ROLLBACK;  
        }  
    }  
  
    @Override  
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {  
        // 回查逻辑  
        return checkOrderStatus(msg);  
    }  
}  

2.5 最大努力通知

该方案是弱一致性方案。

适用于对实时性要求低 的场景(如短信通知):

  1. 业务主流程完成后发送通知
  2. 失败后按策略重试(如间隔1min、5min、10min)
  3. 达到阈值后人工干预
  
// 最大努力通知服务  
publicclass BestEffortNotifier {  
      
    privatestaticfinalint[] RETRY\_INTERVALS = {1, 5, 10, 30, 60}; // 分钟  
      
    public void notify(String event) {  
        int retryCount = 0;  
        while (retryCount < RETRY\_INTERVALS.length) {  
            try {  
                if (sendNotification(event)) {  
                    return; // 通知成功  
                }  
            } catch (Exception e) {  
                // 记录日志  
            }  
            Thread.sleep(RETRY\_INTERVALS[retryCount] * 60 * 1000);  
            retryCount++;  
        }  
        alertManualIntervention(event); // 人工介入  
    }  
}  

实战经验:支付回调采用此方案,重试8次跨12小时,99.5%的通知在30分钟内成功

2.6 Seata AT模式

该方案是自动化的TCC。

Seata的AT(Auto Transaction)模式不侵入业务代码 的前提下实现分布式事务:

核心机制

  1. 全局锁 :TC(事务协调器)管理内存级全局锁(替代数据库行锁)
  2. SQL代理 :解析业务SQL自动生成回滚日志
  3. 二阶段异步提交 :极大提升吞吐量
  
/* 原始SQL */  
UPDATE product SET stock = stock - 10 WHERE id = 1001;  
  
/* Seata自动记录回滚日志 */  
INSERT INTO undo\_log (branch\_id, xid,   
  before\_image, after\_image)   
VALUES (?, ?,   
  '{"stock":100}',  -- 更新前值  
  '{"stock":90}');  -- 更新后值  

性能对比

| 方案 | 锁持有时间 | 锁冲突检测耗时 | 适用场景 | | --- | --- | --- | --- | | 传统2PC | 5002000ms | 520ms | 低并发强一致性 | | Seata AT | 1~10ms | 0.01ms | 高并发最终一致性 |

局限

  • 不支持嵌套事务
  • 热点数据更新冲突率高

2.7 eBay事件队列

该方案是基于本地事务的最终一致性方案。

eBay提出的经典方案:

  1. 将分布式操作拆分为 本地事务+异步事件
  2. 使用 事件表 确保事件不丢失
  3. 通过 补偿机制 解决失败场景
  
-- 订单服务数据库  
BEGIN TRANSACTION;  
-- 1. 创建订单  
INSERT INTO orders (...) VALUES (...);   
-- 2. 记录事件(与订单在同一个事务)  
INSERT INTO event\_queue (event\_type, payload, status)   
VALUES ('ORDER\_CREATED', '{"orderId":1001}', 'PENDING');  
COMMIT;  
  
-- 定时任务扫描事件表并发布  

该方案在早期eBay系统中每天处理1亿+事件,保证核心交易链路最终一致

3.方案的选型指南

根据业务场景选择合适方案:

| 方案 | 一致性级别 | 性能 | 复杂度 | 适用场景 | | --- | --- | --- | --- | --- | | 2PC/3PC | 强一致性 | 低 | 中 | 银行核心系统 | | TCC | 最终一致 | 高 | 高 | 电商交易、积分体系 | | RocketMQ事务消息 | 最终一致 | 高 | 中 | 订单创建、物流通知 | | 最大努力通知 | 弱一致 | 高 | 低 | 短信提醒、运营通知 | | Seata AT | 最终一致 | 高 | 低 | 微服务架构的常规业务 | | eBay事件队列 | 最终一致 | 高 | 中 | 内部状态同步 |

黄金法则

  • 强一致性需求 :选择2PC/ZooKeeper(牺牲性能)
  • 高并发场景 :选择可靠消息/Seata AT(最终一致)
  • 弱一致性场景 :最大努力通知(成本最低)

总结

经过十年演进,分布式事务解决方案已从强一致性高性能最终一致性 发展。

技术没有绝对的好坏,只有适合与否。

我曾见过团队为了追求理论上的强一致性,把系统搞得复杂不堪;也见过过度追求性能导致资金损失的血泪教训。

分布式事务的本质,是在业务需求与技术可行性之间找到平衡点。

致开发者 :不必追求完美的分布式事务解决方案,适合业务场景的才是最好的

在设计时多问自己:

  1. 业务能容忍多长时间不一致?
  2. 事务失败后如何补偿?
  3. 是否有完善的监控和人工介入机制?

愿你在分布式系统的海洋中,乘风破浪,游刃有余。

最后欢迎加入苏三的星球,你将获得:100万QPS短链系统、复杂的商城微服务系统、苏三AI项目、刷题吧小程序、秒杀系统、商城系统、秒杀系统、代码生成工具等8个项目的源代码、开发教程和技术答疑。

系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题、面试八股文等多个优质专栏。

还有1V1免费修改简历、技术答疑、职业规划、送书活动、技术交流。

扫描下方二维码,可以优惠30元:

picture.image

只有20张优惠券,数量有限,先到先得。

目前星球已经更新了5800+篇优质内容,还在持续爆肝中.....

星球已经被官方推荐了3次,收到了小伙伴们的一致好评。戳我加入学习,已有1800+小伙伴加入学习。

0
0
0
0
评论
未登录
暂无评论