ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

分布式事务解决方案Seata实战与原理剖析

分布式事务解决方案Seata实战与原理剖析 1. 为什么我们需要分布式事务在单体应用时代事务管理相对简单。我们使用本地事务就能保证数据一致性比如Spring的Transactional注解。但随着微服务架构的流行一个业务操作往往需要跨多个服务完成这就带来了分布式事务的挑战。想象一下电商系统中的下单场景订单服务创建订单、库存服务扣减库存、支付服务处理支付。这三个操作必须全部成功或全部失败否则就会出现订单创建成功但库存未扣减或者库存扣减了但支付失败等数据不一致的情况。这就是典型的分布式事务问题。分布式事务的核心难点在于CAP理论中的一致性与可用性的权衡。在分布式系统中网络分区不可避免如何在保证系统可用性的同时确保数据一致性是分布式事务要解决的根本问题。2. 主流分布式事务方案对比目前业界主流的分布式事务解决方案主要有以下几种2.1 2PC/XA协议两阶段提交是最经典的分布式事务协议分为准备阶段和提交/回滚阶段。它的优点是强一致性保证但缺点也很明显同步阻塞参与者在准备阶段后必须等待协调者指令期间资源被锁定单点问题协调者宕机会导致系统阻塞数据不一致风险在第二阶段协调者发送提交指令后如果部分参与者收不到指令会导致数据不一致2.2 TCC模式TCCTry-Confirm-Cancel是一种补偿型事务模型将业务操作分为三个阶段Try预留业务资源Confirm确认执行业务操作Cancel取消业务操作释放预留资源TCC的优点是不锁定资源性能较好。但缺点是实现复杂需要为每个业务操作编写Try/Confirm/Cancel三个接口。2.3 本地消息表基本思路是将分布式事务拆分为多个本地事务通过消息队列实现最终一致性。具体流程业务操作和消息写入本地数据库同一个事务定时任务扫描消息表将消息发送到MQ消费者处理消息完成后续操作这种方案实现简单但消息处理可能重复需要保证幂等性。2.4 SAGA模式SAGA将长事务拆分为多个本地事务每个本地事务都有对应的补偿操作。如果某个步骤失败就执行前面步骤的补偿操作。SAGA适合业务流程长、参与者多的场景但补偿逻辑的实现较为复杂。3. Seata架构与核心概念SeataSimple Extensible Autonomous Transaction Architecture是阿里巴巴开源的分布式事务解决方案支持AT、TCC、SAGA和XA四种模式。我们先了解它的核心架构3.1 Seata三大组件Transaction Coordinator (TC)事务协调器维护全局事务的运行状态负责协调并驱动全局事务的提交或回滚。Transaction Manager (TM)事务管理器定义全局事务的范围开始全局事务、提交或回滚全局事务。Resource Manager (RM)资源管理器管理分支事务处理的资源与TC交谈以注册分支事务和报告分支事务的状态并驱动分支事务提交或回滚。3.2 Seata的AT模式原理ATAuto Transaction模式是Seata的默认模式对业务无侵入工作原理如下一阶段解析SQL生成前置镜像before image和后置镜像after image执行业务SQL提交本地事务前向TC注册分支事务本地事务提交二阶段如果全局事务成功TC异步删除undo_log如果全局事务失败TC通知各分支根据undo_log进行补偿3.3 Seata的事务ID设计Seata的事务ID由三部分组成IP:端口全局事务ID这种设计保证了事务ID在分布式环境中的唯一性。4. Seata AT模式实战下面我们通过一个订单-库存的分布式事务案例演示如何使用Seata AT模式。4.1 环境准备首先需要部署Seata Server可以使用Docker快速启动docker run --name seata-server \ -p 8091:8091 \ -e SEATA_IPyour_server_ip \ -e SEATA_PORT8091 \ seataio/seata-server:1.4.24.2 项目配置在Spring Boot项目中引入Seata依赖dependency groupIdio.seata/groupId artifactIdseata-spring-boot-starter/artifactId version1.4.2/version /dependency配置application.ymlseata: enabled: true application-id: order-service tx-service-group: my_tx_group service: vgroup-mapping: my_tx_group: default grouplist: default: 127.0.0.1:8091 registry: type: file4.3 业务代码实现在订单服务中创建全局事务GlobalTransactional public void createOrder(OrderDTO orderDTO) { // 1. 扣减库存 storageFeignClient.deduct(orderDTO.getCommodityCode(), orderDTO.getCount()); // 2. 创建订单 orderMapper.create(orderDTO); // 3. 扣减余额 accountFeignClient.debit(orderDTO.getUserId(), orderDTO.getMoney()); }在库存服务中实现分支事务Transactional public void deduct(String commodityCode, int count) { // 检查库存 Storage storage storageMapper.selectByCommodityCode(commodityCode); if(storage.getCount() count) { throw new RuntimeException(库存不足); } // 扣减库存 storageMapper.deduct(commodityCode, count); }4.4 数据库表设计每个业务数据库都需要创建undo_log表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) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8;5. Seata TCC模式实战对于需要更高性能的场景可以使用TCC模式。下面我们改造库存服务实现TCC接口。5.1 定义TCC接口LocalTCC public interface StorageTccService { TwoPhaseBusinessAction(name deduct, commitMethod confirm, rollbackMethod cancel) boolean prepare(BusinessActionContext actionContext, BusinessActionContextParameter(paramName commodityCode) String commodityCode, BusinessActionContextParameter(paramName count) int count); boolean confirm(BusinessActionContext actionContext); boolean cancel(BusinessActionContext actionContext); }5.2 实现TCC逻辑Service public class StorageTccServiceImpl implements StorageTccService { Autowired private StorageMapper storageMapper; Autowired private StorageFreezeMapper freezeMapper; Transactional Override public boolean prepare(BusinessActionContext actionContext, String commodityCode, int count) { // 检查库存 Storage storage storageMapper.selectByCommodityCode(commodityCode); if(storage.getCount() count) { throw new RuntimeException(库存不足); } // 冻结库存 StorageFreeze freeze new StorageFreeze(); freeze.setXid(actionContext.getXid()); freeze.setCommodityCode(commodityCode); freeze.setCount(count); freeze.setCreateTime(new Date()); freezeMapper.insert(freeze); return true; } Transactional Override public boolean confirm(BusinessActionContext actionContext) { // 获取冻结记录 String xid actionContext.getXid(); StorageFreeze freeze freezeMapper.selectByXid(xid); if(freeze null) { return true; } // 扣减实际库存 storageMapper.deduct(freeze.getCommodityCode(), freeze.getCount()); // 删除冻结记录 freezeMapper.deleteByXid(xid); return true; } Transactional Override public boolean cancel(BusinessActionContext actionContext) { // 删除冻结记录 String xid actionContext.getXid(); freezeMapper.deleteByXid(xid); return true; } }6. Seata性能优化与最佳实践6.1 配置优化在seata-server的file.conf中调整以下参数store { mode db db { datasource druid db-type mysql url jdbc:mysql://127.0.0.1:3306/seata user root password password min-conn 5 max-conn 30 global.table global_table branch.table branch_table lock-table lock_table query-limit 100 } } transport { thread-factory { boss-thread-prefix NettyBoss worker-thread-prefix NettyServerNIOWorker server-executor-thread-prefix NettyServerBizHandler share-boss-worker false client-selector-thread-prefix NettyClientSelector client-selector-thread-size 1 client-worker-thread-prefix NettyClientWorkerThread worker-thread-size default boss-thread-size 1 } shutdown { wait 3 } type TCP server NIO heartbeat true serialization seata compressor none }6.2 常见问题解决全局锁冲突现象出现Global lock wait timeout错误解决方案增加锁等待时间client.rm.lock.retryInterval10默认10ms增加重试次数client.rm.lock.retryTimes30默认30次优化业务逻辑减少事务持有时间AT模式不生效检查点确保数据源被Seata代理检查undo_log表是否存在确认GlobalTransactional注解生效性能瓶颈优化建议使用TCC模式替代AT模式分库分表减少单表数据量调整TC的线程池大小7. Seata与其他框架集成7.1 与Spring Cloud集成在Spring Cloud项目中需要额外配置spring: cloud: alibaba: seata: tx-service-group: my_tx_group7.2 与Dubbo集成在Dubbo服务中需要添加过滤器dubbo:reference filter-exception ... /7.3 与MyBatis集成确保使用Seata的数据源代理Configuration public class DataSourceConfig { Bean ConfigurationProperties(prefix spring.datasource) public DruidDataSource druidDataSource() { return new DruidDataSource(); } Primary Bean(dataSource) public DataSource dataSource(DruidDataSource druidDataSource) { return new DataSourceProxy(druidDataSource); } }8. 生产环境部署建议高可用部署TC服务部署至少3个节点使用Nacos等注册中心做服务发现配置负载均衡监控与告警集成Prometheus监控配置关键指标告警事务成功率平均处理时间失败事务数灾备方案定期备份事务日志准备手动干预脚本设计降级方案在实际项目中我们团队发现Seata的AT模式在中小型系统中表现良好但当并发量超过2000TPS时建议考虑TCC模式。另外全局锁的设计使得它不适合秒杀等高并发场景这类场景更适合使用最终一致性方案。
返回列表