【数据库知识】MySQL 分布式事务实现详解:从简单到复杂的方案对比

MySQL 分布式事务实现详解:从简单到复杂的方案对比

MySQL 分布式事务 Java 实现详解:从简单到复杂方案

分布式事务指跨多个 MySQL 节点(或资源管理器)的事务,需保证“全部成功或全部回滚”。以下按 实现复杂度从简单到困难 排序,用 Java 语言 实现 5 种主流方案,涵盖核心原理、代码示例及关键注意事项。

一、方案 1:MySQL 原生 XA 事务(两阶段提交,强一致性)

复杂度:★☆☆☆☆(最简单,依赖 MySQL XA 协议)
原理:基于 XA 两阶段提交(2PC),Java 通过 javax.sql.XADataSource 接口协调多个 MySQL 实例,事务管理器(TM)由应用层或中间件担任。

Java 实现步骤
1. 依赖准备
  • MySQL 驱动:mysql-connector-java:8.0+
  • 事务管理器:Java 自带 JTA(需容器支持,如 JBoss;或用轻量级 TM 如 Atomikos)
2. 核心代码(模拟跨两个 MySQL 节点)
import com.mysql.cj.jdbc.MysqlXADataSource;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class XATransactionDemo {
    // 模拟两个 MySQL 节点(Node1: 3306,Node2: 3307)
    static class NodeConfig {
        String url;
        String username;
        String password;
        XADataSource xaDataSource;

        public NodeConfig(String url, String user, String pwd) {
            this.url = url;
            this.username = user;
            this.password = pwd;
            MysqlXADataSource ds = new MysqlXADataSource();
            ds.setUrl(url);
            ds.setUser(user);
            ds.setPassword(pwd);
            this.xaDataSource = ds;
        }
    }

    public static void main(String[] args) throws Exception {
        // 1. 配置两个 MySQL 节点
        NodeConfig node1 = new NodeConfig(
            "jdbc:mysql://localhost:3306/db_node1?useSSL=false",
            "root", "123456"
        );
        NodeConfig node2 = new NodeConfig(
            "jdbc:mysql://localhost:3307/db_node2?useSSL=false",
            "root", "123456"
        );

        // 2. 生成全局事务 ID(XID)
        byte[] globalId = "global_tx_001".getBytes();
        int formatId = 1;
        Xid xid = new CustomXid(globalId, formatId); // 自定义 Xid 实现

        Connection conn1 = null, conn2 = null;
        XAConnection xaConn1 = null, xaConn2 = null;
        try {
            // 3. 获取 XA 连接并执行 SQL
            xaConn1 = node1.xaDataSource.getXAConnection();
            conn1 = xaConn1.getConnection();
            XAResource xaRes1 = xaConn1.getXAResource();
            xaRes1.start(xid, XAResource.TMNOFLAGS); // 开启 XA 事务
            executeSql(conn1, "INSERT INTO order (id, user_id, amount) VALUES (1, 1001, 99.9)");
            xaRes1.end(xid, XAResource.TMSUCCESS); // 结束 XA 事务

            xaConn2 = node2.xaDataSource.getXAConnection();
            conn2 = xaConn2.getConnection();
            XAResource xaRes2 = xaConn2.getXAResource();
            xaRes2.start(xid, XAResource.TMNOFLAGS);
            executeSql(conn2, "UPDATE stock SET count = count - 1 WHERE product_id = 2001");
            xaRes2.end(xid, XAResource.TMSUCCESS);

            // 4. 两阶段提交:准备阶段
            int prepare1 = xaRes1.prepare(xid);
            int prepare2 = xaRes2.prepare(xid);
            if (prepare1 == XAResource.XA_OK && prepare2 == XAResource.XA_OK) {
                // 5. 提交阶段
                xaRes1.commit(xid, false);
                xaRes2.commit(xid, false);
                System.out.println("XA 事务提交成功");
            } else {
                // 回滚阶段
                xaRes1.rollback(xid);
                xaRes2.rollback(xid);
                System.out.println("XA 事务回滚");
            }
        } catch (Exception e) {
            // 异常回滚
            if (xaConn1 != null) xaConn1.getXAResource().rollback(xid);
            if (xaConn2 != null) xaConn2.getXAResource().rollback(xid);
            e.printStackTrace();
        } finally {
            // 关闭连接
            close(conn1, xaConn1);
            close(conn2, xaConn2);
        }
    }

    // 执行 SQL
    private static void executeSql(Connection conn, String sql) throws SQLException {
        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.executeUpdate();
        }
    }

    // 关闭资源
    private static void close(AutoCloseable... resources) {
        for (AutoCloseable res : resources) {
            if (res != null) try { res.close(); } catch (Exception e) {}
        }
    }

    // 自定义 Xid 实现(简化版)
    static class CustomXid implements Xid {
        private byte[] globalTransactionId;
        private int formatId;

        public CustomXid(byte[] globalId, int formatId) {
            this.globalTransactionId = globalId;
            this.formatId = formatId;
        }

        @Override public int getFormatId() { return formatId; }
        @Override public byte[] getGlobalTransactionId() { return globalTransactionId; }
        @Override public byte[] getBranchQualifier() { return "branch_001".getBytes(); }
    }
}
关键点
  • Xid 唯一性:全局事务 ID 需唯一(可用 UUID 生成)。
  • 异常处理:任一节点准备失败,需回滚所有节点。
  • 依赖:需 MySQL 驱动支持 XA(mysql-connector-java:8.0+)。

二、方案 2:应用层伪分布式事务(本地事务+补偿,最终一致性)

复杂度:★★☆☆☆(需手动记录日志+补偿逻辑)
原理:拆分跨库操作为本地事务,用“事务日志表”记录步骤状态,失败时按日志逆序补偿。

Java 实现步骤
1. 数据库表设计
-- 事务日志表(单库存储)
CREATE TABLE tx_log (
  tx_id VARCHAR(64) PRIMARY KEY,  -- 全局事务 ID
  step INT,                      -- 步骤序号
  action VARCHAR(32),            -- 操作类型(insert/update)
  biz_id VARCHAR(64),            -- 业务 ID(如订单 ID)
  status TINYINT,                -- 0:待执行 1:成功 2:失败
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
2. Java 代码(Spring Boot + MyBatis)
@Service
public class PseudoDistributedTxService {
    @Autowired
    private OrderMapper orderMapper;  // 订单库 Mapper
    @Autowired
    private StockMapper stockMapper;  // 库存库 Mapper
    @Autowired
    private TxLogMapper txLogMapper;  // 事务日志 Mapper

    // 执行分布式事务
    @Transactional(rollbackFor = Exception.class)  // 本地事务(仅日志库)
    public void executeOrderWithStock(String txId) {
        try {
            // 步骤 1:订单库插入订单(本地事务)
            orderMapper.insert(new Order(1L, 1001L, 99.9));
            txLogMapper.insertLog(txId, 1, "insert_order", "1", 1);  // 记录日志

            // 步骤 2:库存库扣减库存(本地事务)
            stockMapper.updateCount(2001L, -1);
            txLogMapper.insertLog(txId, 2, "update_stock", "2001", 1);  // 记录日志
        } catch (Exception e) {
            // 失败时触发补偿
            compensate(txId);
            throw new RuntimeException("事务失败,已补偿");
        }
    }

    // 补偿逻辑(逆序执行)
    private void compensate(String txId) {
        List<TxLog> logs = txLogMapper.selectByTxId(txId, 1);  // 查询成功的步骤
        Collections.reverse(logs);  // 逆序补偿
        for (TxLog log : logs) {
            switch (log.getAction()) {
                case "insert_order":
                    orderMapper.deleteById(Long.parseLong(log.getBizId()));  // 删除订单
                    break;
                case "update_stock":
                    stockMapper.updateCount(Long.parseLong(log.getBizId()), 1);  // 恢复库存
                    break;
            }
            txLogMapper.updateStatus(log.getId(), 2);  // 标记补偿完成
        }
    }
}
关键点
  • 日志表唯一性tx_id + step 唯一,避免重复记录。
  • 补偿幂等性:补偿操作需支持重复执行(如用 status 字段判断)。

三、方案 3:基于消息队列的最终一致性(异步解耦)

复杂度:★★★☆☆(需引入消息队列,如 RabbitMQ)
原理:通过“本地消息表”确保“业务操作+消息发送”原子性,消息队列异步触发远程操作。

Java 实现步骤
1. 依赖准备
  • Spring Boot Starter AMQP(RabbitMQ)
  • MySQL 驱动
2. 核心代码(订单服务+库存服务)

订单服务(生产者)

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private MsgLogMapper msgLogMapper;  // 本地消息表 Mapper
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Transactional(rollbackFor = Exception.class)
    public void createOrder(Order order) {
        // 1. 本地事务:插入订单
        orderMapper.insert(order);
        // 2. 记录消息到本地消息表
        MsgLog msgLog = new MsgLog(UUID.randomUUID().toString(), "order_created", 
                                 JSON.toJSONString(order), 0);
        msgLogMapper.insert(msgLog);
    }

    // 后台线程发送消息(定时任务)
    @Scheduled(fixedRate = 5000)
    public void sendPendingMsgs() {
        List<MsgLog> logs = msgLogMapper.selectByStatus(0);  // 查询待发送消息
        for (MsgLog log : logs) {
            try {
                rabbitTemplate.convertAndSend("order_exchange", "order.created", log.getContent());
                msgLogMapper.updateStatus(log.getId(), 1);  // 标记为已发送
            } catch (Exception e) {
                msgLogMapper.updateStatus(log.getId(), 2);  // 标记为发送失败
            }
        }
    }
}

库存服务(消费者)

@Component
public class StockConsumer {
    @Autowired
    private StockMapper stockMapper;

    @RabbitListener(queues = "stock_queue")
    public void handleOrderCreated(String orderJson) {
        Order order = JSON.parseObject(orderJson, Order.class);
        // 扣减库存(本地事务)
        stockMapper.updateCount(order.getProductId(), -1);
    }
}
关键点
  • 消息可靠性:用本地消息表+定时任务确保消息不丢失。
  • 消费者幂等性:通过订单 ID 去重(如 Redis 记录已处理订单)。

四、方案 4:TCC 模式(Try-Confirm-Cancel,强一致性)

复杂度:★★★★☆(需业务层实现三阶段逻辑)
原理:Try 预留资源,Confirm 确认执行业务,Cancel 取消预留,Java 需手动实现三阶段。

Java 实现步骤
1. TCC 接口定义
public interface TccService {
    // Try 阶段:预留资源
    boolean tryTransfer(Long fromId, Long toId, BigDecimal amount);
    // Confirm 阶段:确认执行
    boolean confirmTransfer(String txId);
    // Cancel 阶段:取消预留
    boolean cancelTransfer(String txId);
}
2. Java 实现(转账场景)
@Service
public class TransferTccService implements TccService {
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private TccLogMapper tccLogMapper;  // TCC 日志表(记录 txId 状态)

    @Override
    @Transactional
    public boolean tryTransfer(Long fromId, Long toId, BigDecimal amount) {
        // 1. 检查余额并冻结
        Account from = accountMapper.selectById(fromId);
        if (from.getBalance().compareTo(amount) < 0) return false;
        accountMapper.updateFreeze(fromId, amount);  // 冻结金额
        // 2. 记录 TCC 日志(状态:TRYING)
        tccLogMapper.insert(new TccLog(UUID.randomUUID().toString(), "transfer", "TRYING"));
        return true;
    }

    @Override
    @Transactional
    public boolean confirmTransfer(String txId) {
        TccLog log = tccLogMapper.selectByTxId(txId);
        if (log == null || !"TRYING".equals(log.getStatus())) return false;
        // 1. 扣减冻结金额
        Account from = accountMapper.selectById(1L);  // 假设 fromId=1
        accountMapper.updateBalance(from.getId(), from.getBalance().subtract(from.getFreeze()));
        accountMapper.updateFreeze(from.getId(), BigDecimal.ZERO);
        // 2. 增加对方余额
        Account to = accountMapper.selectById(2L);  // 假设 toId=2
        accountMapper.updateBalance(to.getId(), to.getBalance().add(amount));
        // 3. 更新日志状态:CONFIRMED
        tccLogMapper.updateStatus(txId, "CONFIRMED");
        return true;
    }

    @Override
    @Transactional
    public boolean cancelTransfer(String txId) {
        TccLog log = tccLogMapper.selectByTxId(txId);
        if (log == null || !"TRYING".equals(log.getStatus())) return false;
        // 1. 释放冻结金额
        Account from = accountMapper.selectById(1L);
        accountMapper.updateFreeze(from.getId(), BigDecimal.ZERO);
        // 2. 更新日志状态:CANCELED
        tccLogMapper.updateStatus(txId, "CANCELED");
        return true;
    }
}
关键点
  • 幂等性:用 txId 判断 Confirm/Cancel 是否已执行。
  • 空回滚:Try 未执行但 Cancel 被调用时,需忽略。

五、方案 5:SAGA 模式(长事务拆分,最终一致性)

复杂度:★★★★★(最复杂,需拆分长事务为子事务)
原理:将长事务拆分为子事务,每个子事务有补偿方法,失败时逆序补偿。

Java 实现步骤
1. 子事务定义
// 子事务接口
public interface SagaStep {
    void execute() throws Exception;  // 执行子事务
    void compensate() throws Exception;  // 补偿子事务
}

// 预订酒店子事务
public class BookHotelStep implements SagaStep {
    @Override public void execute() { /* 调用酒店 API 预订 */ }
    @Override public void compensate() { /* 取消酒店预订 */ }
}

// 购买机票子事务
public class BuyTicketStep implements SagaStep {
    @Override public void execute() { /* 调用机票 API 购买 */ }
    @Override public void compensate() { /* 退机票 */ }
}
2. SAGA 执行器
public class SagaExecutor {
    private List<SagaStep> steps = new ArrayList<>();
    private Map<Integer, Boolean> executedSteps = new HashMap<>();  // 记录执行状态

    public void addStep(SagaStep step) {
        steps.add(step);
    }

    public void run() throws Exception {
        List<Integer> successIndexes = new ArrayList<>();
        try {
            for (int i = 0; i < steps.size(); i++) {
                steps.get(i).execute();  // 执行子事务
                successIndexes.add(i);  // 记录成功步骤
            }
        } catch (Exception e) {
            // 失败时逆序补偿
            for (int i = successIndexes.size() - 1; i >= 0; i--) {
                steps.get(successIndexes.get(i)).compensate();
            }
            throw e;
        }
    }
}
3. 使用示例
public class TravelBookingSaga {
    public static void main(String[] args) throws Exception {
        SagaExecutor executor = new SagaExecutor();
        executor.addStep(new BookHotelStep());
        executor.addStep(new BuyTicketStep());
        executor.run();  // 执行 SAGA 事务
    }
}
关键点
  • 补偿顺序:严格逆序补偿(后进先出)。
  • 状态持久化:用数据库记录子事务状态,支持崩溃恢复。

总结

方案复杂度Java 核心技术适用场景
MySQL XA 事务★☆☆☆☆javax.sql.XADataSource、两阶段提交小规模、强一致、同构 MySQL
应用层伪分布式★★☆☆☆事务日志表、补偿逻辑非核心业务、简单补偿
消息队列最终一致★★★☆☆RabbitMQ/Kafka、本地消息表微服务解耦、异步场景
TCC 模式★★★★☆Try/Confirm/Cancel 三阶段、幂等性核心业务、短事务、强一致
SAGA 模式★★★★★子事务拆分、状态机、逆序补偿长事务、业务流程化、最终一致

选择建议:优先用 XA(简单强一致)或消息队列(解耦),核心业务用 TCC,长事务用 SAGA。Java 实现需注意事务日志、幂等性和异常处理,确保分布式事务可靠性。

结合第三方组件实现分布式事务

MySQL 结合第三方分布式事务组件实现分布式事务详解

在实际生产环境中,纯手动实现分布式事务(如原生 XA、TCC、SAGA)存在开发成本高、易出错、维护复杂等问题。第三方分布式事务组件通过封装底层协调逻辑(如事务管理器、资源管理器、锁机制),大幅简化分布式事务开发。本文以 主流组件 Seata 为例,详细说明如何结合 MySQL 实现分布式事务,并对比其他组件的特点。

一、第三方分布式事务组件概述

第三方组件核心作用是统一管理分布式事务的协调逻辑,提供标准化接口(如注解、API),支持多种事务模式(AT、TCC、SAGA、XA),适配不同业务场景。

主流组件对比
组件核心模式特点适用场景
SeataAT/TCC/SAGA/XA阿里开源,社区活跃,支持多模式,无侵入 AT 模式大多数分布式场景(电商、金融)
HmilyTCC/SAGA轻量级,支持熔断、重试,国产开源中小规模项目,追求轻量灵活
ByteTCCTCC/XA性能优异,支持 Dubbo/Spring Cloud高并发核心业务(如支付、转账)
TCC-TransactionTCC较早开源,文档丰富,需手动实现补偿逻辑熟悉 TCC 模式的团队

二、以 Seata 为例:结合 MySQL 实现分布式事务

Seata(Simple Extensible Autonomous Transaction Architecture)是阿里开源的分布式事务解决方案,支持 AT(Automatic Transaction,无侵入)、TCC、SAGA、XA 四种模式。以下以最常用的 AT 模式(无侵入,适合快速接入)为例,详细说明实现步骤。

核心概念
  • TC(Transaction Coordinator):事务协调者,独立部署的服务,维护全局事务和分支事务状态,驱动提交/回滚。
  • TM(Transaction Manager):事务管理器,嵌入应用中,定义全局事务范围(@GlobalTransactional 注解)。
  • RM(Resource Manager):资源管理器,嵌入应用中,管理分支事务(MySQL 数据库连接),向 TC 汇报状态,执行 undo/redo 日志。
实现步骤
1. 环境准备
  • MySQL:至少 2 个实例(模拟跨库),或单库多表(简化演示)。
  • Seata Server:下载并启动 TC(https://seata.io/zh-cn/blog/download.html)。
  • 注册中心:Seata 需注册中心(如 Nacos、Eureka),这里用 Nacos 示例。
2. 部署 Seata Server
(1)修改 Seata 配置

下载 Seata Server 后,修改 conf/application.yml(以 Nacos 为注册中心):

server:
  port: 7091
spring:
  application:
    name: seata-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # Nacos 地址
        namespace: ""  # 命名空间(默认 public)
seata:
  config:
    type: nacos  # 配置中心用 Nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: ""
  registry:
    type: nacos  # 注册中心用 Nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      namespace: ""
  store:
    mode: file  # 事务日志存储(生产用 db 模式)
(2)启动 Seata Server
# Linux/Mac
sh bin/seata-server.sh -p 8091 -h 127.0.0.1

# Windows
bin/seata-server.bat -p 8091 -h 127.0.0.1
3. MySQL 配置(AT 模式必需)

AT 模式依赖 undo_log 表记录回滚日志,需在参与事务的每个 MySQL 库中创建:

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
4. 项目集成 Seata(Spring Boot 示例)

以“订单-库存”分布式事务为例(订单库 db_order,库存库 db_stock),使用 Seata AT 模式。

(1)添加依赖
<!-- Seata 核心依赖 -->
<dependency>
  <groupId>io.seata</groupId>
  <artifactId>seata-spring-boot-starter</artifactId>
  <version>1.6.1</version>  <!-- 最新稳定版 -->
</dependency>
<!-- Nacos 注册中心(若用其他注册中心需替换) -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>
<!-- MyBatis Plus(简化 ORM) -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.1</version>
</dependency>
(2)配置文件(application.yml
spring:
  application:
    name: order-service  # 服务名(注册到 Nacos)
  datasource:
    # 订单库数据源(需 Seata 代理)
    order:
      url: jdbc:mysql://localhost:3306/db_order?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 库存库数据源(需 Seata 代理)
    stock:
      url: jdbc:mysql://localhost:3307/db_stock?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # Nacos 地址

# Seata 配置
seata:
  application-id: ${spring.application.name}  # 应用 ID(默认服务名)
  tx-service-group: my_test_tx_group  # 事务组名称(需与 Seata Server 配置一致)
  service:
    vgroup-mapping:
      my_test_tx_group: default  # 事务组映射到 Seata Server 集群名(默认 default)
  registry:
    type: nacos
    nacos:
      application: seata-server  # Seata Server 注册到 Nacos 的服务名
      server-addr: 127.0.0.1:8848
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
(3)数据源代理(关键!Seata 需代理数据源)

Seata 通过代理数据源拦截 SQL,生成 undo_log 并记录全局锁。需为每个数据源配置代理:

@Configuration
public class DataSourceProxyConfig {
    // 订单库数据源代理
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DruidDataSource orderDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy orderDataSourceProxy(DruidDataSource orderDataSource) {
        return new DataSourceProxy(orderDataSource);  // Seata 代理
    }

    // 库存库数据源代理(同上)
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.stock")
    public DruidDataSource stockDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy stockDataSourceProxy(DruidDataSource stockDataSource) {
        return new DataSourceProxy(stockDataSource);
    }
}
(4)业务逻辑实现(订单+库存)

订单服务(TM 角色,定义全局事务)

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderMapper orderMapper;  // 订单库 Mapper
    @Autowired
    private StockFeignClient stockFeignClient;  // 库存服务 Feign 客户端

    // 全局事务注解:标记分布式事务入口
    @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public void createOrder(OrderDTO orderDTO) {
        // 1. 本地事务:插入订单(订单库)
        Order order = new Order();
        order.setId(orderDTO.getOrderId());
        order.setUserId(orderDTO.getUserId());
        order.setAmount(orderDTO.getAmount());
        orderMapper.insert(order);

        // 2. 远程调用:扣减库存(库存库,分支事务)
        stockFeignClient.deductStock(orderDTO.getProductId(), 1);
    }
}

库存服务(RM 角色,分支事务)

@Service
public class StockServiceImpl implements StockService {
    @Autowired
    private StockMapper stockMapper;  // 库存库 Mapper

    @Override
    public void deductStock(Long productId, Integer count) {
        // 本地事务:扣减库存(库存库)
        stockMapper.deductStock(productId, count);
    }
}
5. 测试分布式事务
  1. 启动 Nacos、Seata Server、订单服务、库存服务。
  2. 调用订单服务的 createOrder 接口:
    • 若库存扣减成功:全局事务提交,Seata TC 通知 RM 删除 undo_log。
    • 若库存扣减失败(如抛异常):全局事务回滚,Seata TC 通知 RM 执行 undo_log 回滚订单插入。

三、Seata 其他模式简介

1. TCC 模式(手动补偿)

需业务层实现 Try-Confirm-Cancel 接口,Seata 协调三阶段提交:

// TCC 接口示例(库存服务)
@LocalTCC
public interface StockTccService {
    // Try:预留资源(冻结库存)
    @TwoPhaseBusinessAction(name = "deductStockTcc", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryDeduct(@BusinessActionContextParameter(paramName = "productId") Long productId,
                      @BusinessActionContextParameter(paramName = "count") Integer count);

    // Confirm:确认执行业务(扣减冻结库存)
    boolean confirm(BusinessActionContext context);

    // Cancel:取消预留(释放冻结库存)
    boolean cancel(BusinessActionContext context);
}
2. SAGA 模式(长事务拆分)

将长事务拆分为子事务,Seata 记录状态并驱动补偿:

// SAGA 事务配置(通过 JSON 定义子事务和补偿)
{
  "forward": [
    {"name": "createOrder", "service": "orderService", "method": "create"},
    {"name": "deductStock", "service": "stockService", "method": "deduct"}
  ],
  "reverse": [
    {"name": "cancelOrder", "service": "orderService", "method": "cancel"},
    {"name": "restoreStock", "service": "stockService", "method": "restore"}
  ]
}

四、其他第三方组件简要示例

1. Hmily(TCC/SAGA 模式)
  • 特点:轻量级,支持熔断、重试,国产开源。
  • 集成步骤
    1. 引入依赖 org.dromara:hmily-spring-boot-starter
    2. 配置 Hmily Server 地址。
    3. 业务方法添加 @Hmily 注解,实现 TCC 三阶段接口。
2. ByteTCC(TCC/XA 模式)
  • 特点:性能优异,支持 Dubbo/Spring Cloud,字节跳动开源。
  • 集成步骤
    1. 引入依赖 org.bytesoft:bytetcc-core
    2. 配置 ByteTCC 事务管理器。
    3. 业务层实现 Compensable 接口(TCC)或 XATransaction 接口(XA)。

五、总结

第三方分布式事务组件通过封装协调逻辑,大幅降低了 MySQL 分布式事务的开发门槛。Seata 是首选方案,其 AT 模式无侵入、易接入,适合大多数场景;TCC 模式适合核心业务手动控制;SAGA 模式适合长事务。其他组件如 Hmily、ByteTCC 可根据团队熟悉度和性能需求选择。

关键优势

  • 无需手动编写补偿逻辑(AT 模式)。
  • 提供事务监控、重试、熔断等增强功能。
  • 适配多种注册中心和服务框架(Spring Cloud、Dubbo)。

通过组件化方案,可高效实现 MySQL 分布式事务,保障数据一致性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

问道飞鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值