AT模式是Seata框架中的一种分布式事务解决方案,它利用两阶段提交(2PC)的概念,通过日志记录(在undo_log中)来实现在分布式系统中数据的一致性。AT模式可以解决分布式事务中的数据不一致问题,适合于RPC和微服务架构。
工作原理
AT模式通过以下两个阶段来保证分布式事务的一致性:
-
一阶段(Prepare阶段):业务数据和回滚日志(undo_log)记录
- 在业务操作的同时,记录业务操作前后的数据状态,生成回滚日志并保存在undo_log表中。
-
二阶段(Commit/Rollback阶段):事务提交或回滚
- 如果一阶段成功,二阶段将提交所有局部事务,并且异步清除undo_log。
- 如果一阶段中的任何操作失败,二阶段将根据undo_log中的信息回滚已执行的局部事务。
核心组件
- TC (Transaction Coordinator):事务协调器,维护全局事务状态,协调事务的提交或回滚。
- TM (Transaction Manager):事务管理器,定义全局事务的范围,负责开启和结束事务。
- RM (Resource Manager):资源管理器,管理分支事务处理的资源。
示例代码
假设您的系统中有订单服务和库存服务,您希望在创建订单的同时扣减库存。使用Seata进行事务管理的代码如下:
首先,配置Seata代理数据源:
@Configuration
public class DataSourceConfig {@Beanpublic DataSource dataSource(DataSourceProperties properties) {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl(properties.getUrl());dataSource.setUsername(properties.getUsername());dataSource.setPassword(properties.getPassword());// 使用Seata对数据源进行包装return new DataSourceProxy(dataSource);}
}
然后,定义全局事务的业务逻辑:
@Service
public class BusinessService {@Autowiredprivate OrderService orderService;@Autowiredprivate InventoryService inventoryService;@Transactional // 本地事务注解@GlobalTransactional(timeoutMills = 300000, name = "fsp-create-order") // Seata全局事务注解public void purchase(String userId, String commodityCode, int orderCount) {// 1. 创建订单orderService.createOrder(userId, commodityCode, orderCount);// 2. 扣减库存inventoryService.deduct(commodityCode, orderCount);}
}
业务服务中,订单服务和库存服务的实现可能如下:
public class OrderService {// 注入数据源或使用JdbcTemplate等public void createOrder(String userId, String commodityCode, int orderCount) {// 实现创建订单的逻辑// 此处会有数据库操作,例如插入订单记录}
}public class InventoryService {// 注入数据源或使用JdbcTemplate等public void deduct(String commodityCode, int orderCount) {// 实现扣减库存的逻辑// 此处会有数据库操作,例如更新库存数量}
}
细节分析
在使用AT模式时,需要注意以下关键点:
- undo_log表:确保数据库中有undo_log表,用于记录数据变更日志。
- 隔离级别:AT模式需要合适的隔离级别来避免脏读、幻读等问题。
- 幂等性:为了防止重复执行,需要确保业务逻辑的幂等性。
- 超时控制:设置合适的超时时间是重要的,防止事务挂起过久影响系统性能。
- 资源锁:Seata会锁定事务涉及的资源,需关注锁的粒度和持有时间。
- 服务降级:在Seata服务不可用时,应有降级策略保证业务可继续执行。
注意
实际部署Seata和AT模式,需要详细阅读Seata官方文档,并根据具体环境进行配置。以上代码演示是一个简化的例子,实际应用可能涉及更复杂的配置和错误处理策略。