以下是一个基于 Seata框架 的分布式事务实战 Demo,使用 AT模式(Automatic Transaction) 实现类似 3PC 的效果。AT 模式是 Seata 的核心模式,结合了 2PC 和乐观锁,通过自动记录 Undo Log 实现高效回滚。
环境准备
-
下载 Seata Server
访问 Seata官网 下载最新版(以 2.1.0 为例),解压后启动:# 启动 TC(Transaction Coordinator) sh seata-server.bat -p 8091 -h 127.0.0.1# 启动 TM(Transaction Manager)和 RM(Resource Manager) sh seata-server.bat -p 8092 -h 127.0.0.1
-
配置数据库
创建 MySQL 数据库并初始化undo_log
表(Seata 回滚依赖):CREATE DATABASE seata_order DEFAULT CHARSET=utf8mb4; USE seata_order;CREATE TABLE `order` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT,`user_id` VARCHAR(50),`amount` DECIMAL(10,2),`status` ENUM('CREATED', 'COMMITTED', 'ROLLED_BACK') DEFAULT 'CREATED' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `inventory` (`sku` VARCHAR(50) PRIMARY KEY,`stock` INT DEFAULT 100 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- undo_log 表(Seata 自动创建) CREATE TABLE `undo_log` (`branch_transaction_id` BIGINT PRIMARY KEY,`xid` VARCHAR(100),`rollback_info` LONGBLOB,`log_table_name` VARCHAR(100),`log_table_column` VARCHAR(100),`log_key` VARCHAR(200),`timestamp` DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
注册中心与服务发现
使用 Nacos 作为注册中心,启动 Nacos Server 并配置 Seata Server 的application.yml
:# seata-server/config/application.yml spring:application:name: seata-serverdatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_order?useSSL=false&serverTimezone=UTCusername: rootpassword: rootregistry:type: nacosnacos:server-addr: 127.0.0.1:8848config:center:type: nacosnacos:server-addr: 127.0.0.1:8848
Spring Boot 项目集成 Seata
1. 添加依赖
在 pom.xml
中添加 Seata 和 MySQL 驱动依赖:
<dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>2.1.0</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
2. 配置数据源代理
在 application.yml
中配置 Seata 数据源代理:
spring:datasource:url: jdbc:mysql://localhost:3306/seata_order?useSSL=false&serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:max-active: 20seata:tx-service-group: my_test_tx_groupenable-auto-commit: falseservice-component:tx-manager:service-name: seata-tm-serviceregister-center:type: nacosnacos:server-addr: 127.0.0.1:8848tx-coordinator:service-name: seata-tc-serviceregister-center:type: nacosnacos:server-addr: 127.0.0.1:8848
3. 启用全局事务注解
在启动类上添加 @EnableSeataTx
注解:
@SpringBootApplication
@EnableSeataTx
public class SeataOrderApplication {public static void main(String[] args) {SpringApplication.run(SeataOrderApplication.class, args);}
}
业务代码实现
1. 订单服务(OrderService)
@Service
public class OrderService {@Autowiredprivate OrderDAO orderDAO;@Autowiredprivate InventoryService inventoryService;@Autowiredprivate PaymentService paymentService;@GlobalTransactional(timeout=30000, name="createOrder", rollbackFor=Exception.class)public void createOrder(Order order) {// 1. 扣减库存inventoryService.deduct(order.getSkuId());// 2. 扣款(支付服务调用)paymentService.charge(order.getUserId(), order.getAmount());// 3. 生成订单orderDAO.insert(order);}
}
2. 库存服务(InventoryService)
@Service
public class InventoryService {@Autowiredprivate InventoryDAO inventoryDAO;@Transactionalpublic void deduct(String sku) {Inventory inventory = inventoryDAO.findBySku(sku);if (inventory.getStock() <= 0) {throw new RuntimeException("库存不足,SKU: " + sku);}inventory.setStock(inventory.getStock() - 1);inventoryDAO.update(inventory);}
}
3. 支付服务(PaymentService)
@Service
public class PaymentService {@Autowiredprivate PaymentDAO paymentDAO;@Transactionalpublic void charge(String userId, BigDecimal amount) {Payment payment = paymentDAO.findByUserId(userId);if (payment == null) {throw new RuntimeException("用户未找到,ID: " + userId);}payment.setBalance(payment.getBalance().subtract(amount));paymentDAO.update(payment);}
}
4. 数据库操作类
OrderDAO
@Repository
public class OrderDAO {@Autowiredprivate JdbcTemplate jdbcTemplate;public void insert(Order order) {String sql = "INSERT INTO `order` (user_id, amount, status) VALUES (?, ?, 'CREATED')";jdbcTemplate.update(sql, order.getUserId(), order.getAmount());}
}
InventoryDAO
@Repository
public class InventoryDAO {@Autowiredprivate JdbcTemplate jdbcTemplate;public Inventory findBySku(String sku) {String sql = "SELECT * FROM `inventory` WHERE sku = ?";return jdbcTemplate.queryForObject(sql, Inventory.class, sku);}public void update(Inventory inventory) {String sql = "UPDATE `inventory` SET stock = ? WHERE sku = ?";jdbcTemplate.update(sql, inventory.getStock(), inventory.getSku());}
}
5. 测试与验证
1. 触发全局事务
创建 OrderController
提供测试接口:
@RestController
@RequestMapping("/orders")
public class OrderController {@Autowiredprivate OrderService orderService;@PostMapping("/create")public String createOrder(@RequestBody Order order) {try {orderService.createOrder(order);return "订单创建成功!";} catch (Exception e) {return "订单创建失败:" + e.getMessage();}}
}
2. 模拟失败场景
在 PaymentService
中故意抛出异常:
public void charge(String userId, BigDecimal amount) {// 模拟支付失败if (Math.random() > 0.5) {throw new RuntimeException("支付失败!");}// 正常逻辑...
}
3. 验证回滚
• 成功场景:所有服务正常,订单提交成功。
• 失败场景:支付失败,触发全局回滚:
• 库存恢复(inventory
表的 stock
加回)。
• 订单状态保持为 CREATED
。
• 支付记录未提交(需手动回滚,由 Seata 自动处理)。
关键原理与优势
1. Seata AT模式的核心流程
• 阶段一(Branch Registration):
• TM(订单服务)启动全局事务,向 TC 注册分支事务。
• 阶段二(Local Transaction):
• 各个 RM(库存、支付、订单服务)执行本地事务,自动记录 Undo Log。
• 阶段三(Commit/Rollback):
• TC 收集所有分支事务状态,决定提交或回滚:
◦ 提交:各 RM 提交本地事务。
◦ 回滚:通过 Undo Log 恢复数据。
2. 优势对比传统2PC
• 无代码侵入:通过注解 @GlobalTransactional
简化开发。
• 高性能:基于乐观锁和异步通信,减少同步阻塞。
• 自动补偿:无需手动编写补偿逻辑(如 TCC 的 Cancel
方法)。
常见问题与解决
1. 事务未提交/回滚
• 检查点:
• 确保 @GlobalTransactional
注解正确标注在 TM 方法上。
• 确认 Seata Server 和 Nacos 服务正常运行。
2. 数据不一致
• 解决方案:
• 检查 undo_log
表是否正常记录回滚信息。
• 验证本地事务的 @Transactional
是否生效。
3. 超时问题
• 调整配置:
• 在 application.yml
中设置 global.tx.timeout=30000
(默认 30 秒)。
• 优化业务逻辑,减少事务执行时间。
总结
通过 Seata 的 AT 模式,我们无需手动实现复杂的 2PC 或 3PC 协议,即可轻松获得分布式事务的强一致性保障。其核心优势在于:
• 开发成本低:注解驱动,减少模板代码。
• 高性能:异步回滚机制,降低资源阻塞。
• 高可用:集群化部署,避免单点故障。
适用场景:
• 电商订单、金融转账等需要强一致性的场景。
• 微服务架构中需跨服务事务的场景。
进阶方向:
• 结合 Saga 模式处理长事务。
• 通过 Seata 的 @Compensable
注解实现自定义补偿逻辑。