Seata AT模式:百万订单下的跨服务一致性实战指南
2025-06-25 13:03 阅读(33)

分布式事务就像一场没有司仪的集体婚礼——新郎(订单服务)说"我愿意"时,新娘(库存服务)可能正在后台修改逃跑路线。别慌,Seata AT就是你的婚礼拯救大师!


AT模式核心三幕剧

百万级订单的生存法则


全局锁优化:用Redis替代DB锁,TP99降低40ms

TC分片路由:按xid哈希路由到不同TC集群

异步化提交:二阶段提交转异步队列(慎用!)

Saga模式降级:超时订单转补偿流程


手撕代码时刻(Spring Boot + Seata 1.8.0)

订单服务:

@RestController
public class OrderController {
    
    @GlobalTransactional(name = "create-order-tx", timeoutMills = 60000)
    @PostMapping("/order")
    public String createOrder(@RequestBody OrderDTO dto) {
        // 1. 扣减库存(跨服务调用)
        inventoryFeignClient.deduct(dto.getSkuId(), dto.getCount());
        
        // 2. 本地创建订单
        orderService.create(dto); // 伪代码:insert into orders...
        
        // 测试回滚:手动触发异常
        if(dto.getCount() > 1000) throw new RuntimeException("✨ 表演一个事务回滚 ✨");
        
        return "Order-2025" + System.currentTimeMillis();
    }
}


库存服务:

@Service
public class InventoryServiceImpl {

    @Transactional
    public void deduct(String skuId, int count) {
        // 关键操作:Seata自动代理数据源
        jdbcTemplate.update(
            "UPDATE inventory SET stock = stock - ? WHERE sku_id = ? AND stock >= ?",
            count, skuId, count
        );
        
        // Seata自动记录undo_log:
        // {"before":{"stock":100},"after":{"stock":90}}
    }
}

性能压测数据(双机房部署)


-并发量传统XASeata AT提升
1万32秒11秒3x
10万超时失败98秒
100万-22分钟

   

避坑了避坑了 AT不是银弹



隔离级别:  默认是读未提交(Read Uncommitted)。高并发扣库存时,可能出现脏读(看到未提交的库存减少)。解决方案:


业务上使用SELECT ... FOR UPDATE(牺牲性能)。

改用TCC/SAGA模式(业务侵入性强)。

设计幂等接口,结合状态机处理中间状态。




全局锁冲突:


避免长时间持有全局锁(分支事务内不宜处理耗时远程调用)。

热点数据(如秒杀商品)考虑分片键设计,将库存分散到不同记录(如10条记录各存100件)。




非SQL操作:  AT模式强依赖SQL解析。对Redis、MQ等非SQL操作无效!需结合TCC或消息事务。



终极方案:混合事务模式

// 在AT事务中嵌套Saga
@GlobalTransactional
public void hybridTransaction() {
    atTransaction(); // AT操作
    
    // 调用Saga参与者
    StartSagaBuilder.newBuilder()
        .withService(serviceA)
        .withCompensation(serviceACompensation);
}


Demo验证技巧

# 强制触发回滚(测试用)
curl -X POST http://order-service/order \
  -H "Content-Type: application/json" \
  -d '{"skuId":"G-777", "count":2000}' 

# 查看undo_log(Seata的时光机)
mysql> select * from undo_log \G
*************************** 1. row ***************************
    branch_id: 725883267
    xid: 10.16.xx.xx:8091:725883266
    rollback_info: {"@class":"io.seata.rm.datasource.undo...."}


输出回滚日志即证明事务生效!


最后三条保命建议

生产环境必须开TC集群(单节点是作死行为)

更新语句一定加WHERE条件限制范围

重要业务搭配Saga模式做补偿事务(双重保险)