找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1123

积分

0

好友

155

主题
发表于 昨天 18:07 | 查看: 3| 回复: 0

一、Seata核心架构与定位

Seata(Simple Extensible Autonomous Transaction Architecture)是一款由阿里巴巴开源的分布式事务解决方案,旨在为微服务架构提供高性能且易于集成的分布式事务能力。

Seata的架构演进

Seata架构演进图

Seata核心角色解析

角色 简称 职责 类比
事务协调器 TC (Transaction Coordinator) 维护全局和分支事务的状态,驱动全局事务的提交或回滚。 裁判
事务管理器 TM (Transaction Manager) 定义全局事务的边界,负责开启、提交或回滚全局事务。 教练
资源管理器 RM (Resource Manager) 管理分支事务,负责分支事务的注册、状态报告,并驱动分支事务的提交和回滚。 运动员

二、Seata四种事务模式对比

特性 AT模式 TCC模式 Saga模式 XA模式
原理 基于SQL解析的自动补偿 Try-Confirm-Cancel三阶段 状态机 + 补偿事务 XA两阶段提交
侵入性 ⭐ 无侵入(代理数据源) ⭐⭐⭐⭐ 高侵入(需实现三个接口) ⭐⭐⭐ 中侵入(需定义状态机) ⭐ 无侵入
性能 ⭐⭐⭐⭐ 高 ⭐⭐⭐⭐ 高 ⭐⭐⭐ 中 ⭐⭐ 低
一致性 最终一致性 强一致性 最终一致性 强一致性
适用场景 大部分常规业务场景 金融、资金交易等强一致性场景 长事务、跨系统业务流程 传统数据库事务迁移

三、Seata AT模式深度解析

1. AT模式工作原理

AT模式是一种两阶段提交的增强实现,其核心在于一阶段即提交本地事务,并通过Undo Log机制保证二阶段的可回滚性。

AT模式两阶段流程

2. AT模式核心实现机制

AT模式的自动补偿能力建立在SQL拦截和镜像生成之上。

/**
 * AT模式数据源代理
 * 通过DataSourceProxy拦截所有SQL执行
 */
public class DataSourceProxy extends AbstractDataSourceProxy {
    private final DataSource targetDataSource;

    @Override
    public ConnectionProxy getConnection() throws SQLException {
        Connection targetConnection = targetDataSource.getConnection();
        return new ConnectionProxy(this, targetConnection);
    }
}

/**
 * 连接代理 - 拦截所有SQL执行
 */
public class ConnectionProxy implements Connection {
    private final Connection targetConnection;
    private final List<SQLRecognizer> sqlRecognizers;

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        // 1. SQL解析
        sqlRecognizers = SQLVisitorFactory.get(sql, dbType);

        // 2. 生成前后镜像(Before & After Image)
        for (SQLRecognizer recognizer : sqlRecognizers) {
            switch (recognizer.getSQLType()) {
                case INSERT:
                    TableRecords afterImage = buildTableRecords(recognizer);
                    break;
                case UPDATE:
                    TableRecords beforeImage = queryBeforeImage(recognizer);
                    TableRecords afterImage = queryAfterImage(recognizer, beforeImage);
                    break;
                case DELETE:
                    TableRecords beforeImage = queryBeforeImage(recognizer);
                    break;
            }
        }

        // 3. 执行业务SQL
        PreparedStatement targetPreparedStatement = targetConnection.prepareStatement(sql);
        return new PreparedStatementProxy(this, targetPreparedStatement, recognizer);
    }
}

/**
 * Undo Log核心数据结构
 */
public class UndoLog {
    private Long id;
    private String xid;           // 全局事务ID
    private Long branchId;        // 分支事务ID
    private String rollbackInfo;  // 序列化后的回滚信息(前后镜像)
    private Integer logStatus;    // 状态
    private Date logCreated;
    private Date logModified;
}

3. AT模式实战配置与示例

Client端配置 (application.yml

# Seata Client 配置
seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_tx_group
  enable-auto-data-source-proxy: true # 开启自动数据源代理
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      group: SEATA_GROUP
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: localhost:8848
      group: SEATA_GROUP

# 标准数据源配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/order_db
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

业务代码示例
在基于JavaSpringBoot的微服务中,通过一个注解即可开启全局事务。

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private AccountFeignClient accountClient;
    @Autowired
    private InventoryFeignClient inventoryClient;

    /**
     * 创建订单 - 分布式事务
     * @GlobalTransactional 注解标记此方法为一个全局事务的起点
     */
    @GlobalTransactional(name = "create-order", timeoutMills = 300000)
    public void createOrder(OrderDTO orderDTO) {
        log.info("======> 开始创建订单,全局事务XID: {}", RootContext.getXID());

        // 阶段一:本地操作
        Order order = createOrderLocal(orderDTO);

        // 阶段二:远程调用 - 扣减库存
        ResponseEntity<Void> inventoryResponse = inventoryClient.deduct(
                orderDTO.getProductId(),
                orderDTO.getQuantity()
        );
        if (!inventoryResponse.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("库存扣减失败,触发全局回滚");
        }

        // 阶段三:远程调用 - 扣减余额
        ResponseEntity<Void> accountResponse = accountClient.deduct(
                orderDTO.getUserId(),
                orderDTO.getAmount()
        );
        if (!accountResponse.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("余额扣减失败,触发全局回滚");
        }

        // 所有操作成功,更新订单状态
        updateOrderStatus(order.getId(), OrderStatus.SUCCESS);
        log.info("======> 订单创建成功,全局事务提交");
    }

    private Order createOrderLocal(OrderDTO orderDTO) {
        // ... 本地创建订单逻辑
        orderMapper.insert(order);
        return order;
    }
}

Feign客户端配置(传递XID)

@Configuration
public class FeignConfig {
    @Bean
    public RequestInterceptor seataFeignInterceptor() {
        return template -> {
            // 将当前全局事务XID传递到下游服务
            String xid = RootContext.getXID();
            if (StringUtils.isNotBlank(xid)) {
                template.header("TX_XID", xid);
            }
        };
    }
}

四、TCC模式深度解析与实战

1. TCC模式实现

TCC模式要求开发者显式实现Try、Confirm、Cancel三个业务逻辑。

/**
 * TCC订单服务接口
 */
public interface TccOrderService {
    @TwoPhaseBusinessAction(name = "orderService", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryCreateOrder(@BusinessActionContextParameter(paramName = "order") OrderDTO order);
    boolean confirm(BusinessActionContext actionContext);
    boolean cancel(BusinessActionContext actionContext);
}

@Service
public class TccOrderServiceImpl implements TccOrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Override
    public boolean tryCreateOrder(OrderDTO orderDTO) {
        log.info("TCC Try阶段:预创建订单(资源预留)");
        // 幂等性检查,防止重复执行
        Order existOrder = orderMapper.selectByOrderNo(orderDTO.getOrderNo());
        if (existOrder != null) {
            log.warn("订单已存在,直接返回成功");
            return true;
        }
        // 创建状态为TRY(尝试中)的订单,并设置过期时间
        Order order = new Order();
        order.setStatus(OrderStatus.TRY);
        order.setExpireTime(DateUtils.addMinutes(new Date(), 30));
        orderMapper.insert(order);
        return true;
    }

    @Override
    public boolean confirm(BusinessActionContext actionContext) {
        log.info("TCC Confirm阶段:确认订单");
        String orderNo = (String) actionContext.getActionContext("orderNo");
        // 将订单状态从TRY更新为CONFIRMED
        int result = orderMapper.updateStatusByOrderNo(orderNo, OrderStatus.TRY, OrderStatus.CONFIRMED);
        return result > 0;
    }

    @Override
    public boolean cancel(BusinessActionContext actionContext) {
        log.info("TCC Cancel阶段:取消订单");
        String orderNo = (String) actionContext.getActionContext("orderNo");
        // 空回滚检查:如果Try没执行,直接记录空回滚并返回
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (order == null) {
            recordEmptyRollback(orderNo); // 记录防悬挂
            return true;
        }
        // 正常回滚:将订单状态从TRY更新为CANCELLED
        int result = orderMapper.updateStatusByOrderNo(orderNo, OrderStatus.TRY, OrderStatus.CANCELLED);
        return result > 0;
    }
}

2. TCC防悬挂与空回滚解决方案

@Component
public class TccProblemSolver {
    /**
     * 防悬挂检查:在Try阶段执行前调用
     * 若发现已存在针对该业务键的空回滚记录,则说明发生了悬挂(Cancel先于Try到达)
     */
    public void checkHangingBeforeTry(String businessKey) {
        if (isEmptyRollback(businessKey)) {
            throw new RuntimeException("检测到悬挂风险,拒绝执行Try操作,业务键:" + businessKey);
        }
    }

    private boolean isEmptyRollback(String businessKey) {
        // 查询空回滚记录表,判断此业务键是否已执行过空回滚
        return emptyRollbackMapper.existsByBusinessKey(businessKey);
    }
}

五、Seata Server部署与高可用配置

1. Server端核心配置 (application.yml)

server:
  port: 7091
seata:
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      group: SEATA_GROUP
  registry:
    type: nacos
    nacos:
      application: seata-server
      server-addr: localhost:8848
      group: SEATA_GROUP
  store:
    mode: db # 事务日志存储模式,支持db、file、redis等
    db:
      datasource: druid
      db-type: mysql
      url: jdbc:mysql://localhost:3306/seata?useUnicode=true
      user: root
      password: root

2. 生产环境高可用部署 (docker-compose.yml)

通过Nginx负载均衡部署多个Seata Server实例,实现高可用。

version: '3.8'
services:
  seata-server1:
    image: seataio/seata-server:1.5.0
    container_name: seata-server1
    environment:
      - SEATA_IP=seata-server1
      - STORE_MODE=db
      - DB_HOST=mysql
  seata-server2:
    image: seataio/seata-server:1.5.0
    container_name: seata-server2
    environment:
      - SEATA_IP=seata-server2
      - STORE_MODE=db
      - DB_HOST=mysql
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf # Nginx负载均衡配置

六、生产环境最佳实践与调优

1. 性能优化配置参数

seata:
  client:
    rm:
      report-success-enable: false # 分支事务一阶段成功是否立即上报,关闭以减少网络开销
      async-commit-buffer-limit: 10000 # 异步提交缓冲区大小
    tm:
      commit-retry-count: 5 # 全局事务提交重试次数
    undo:
      data-validation: true # 开启Undo Log数据校验,防止脏数据
  transport:
    enable-client-batch-send-request: true # 启用客户端批量发送请求
    compression: gzip # 启用网络传输压缩

2. 关键监控指标

建议监控以下核心指标以保障稳定性:

  • 全局事务成功率:衡量系统事务健康度的核心指标。
  • 分支事务平均耗时:定位性能瓶颈。
  • 全局锁冲突次数:高并发下AT模式需重点关注,冲突过多可能影响吞吐量。
  • Undo Log积压数量:反映二阶段清理是否正常。

七、面试要点深度解析

核心回答思路
在微服务架构中,Seata通过TC、TM、RM三组件协作解决数据一致性问题。选择AT还是TCC模式是关键决策点:AT模式无侵入,通过SQL镜像实现自动回滚,适合大部分对最终一致性容忍度高的业务;TCC模式侵入性强,需要编码实现三阶段逻辑,但能保证强一致性,适用于交易、金融等核心场景。

高频深入问题

  1. AT模式如何解决脏写?
    通过全局锁机制。在一阶段,RM在操作数据前会向TC申请相关记录的全局锁。其他事务若要修改同一记录,必须等待该锁释放,从而避免了脏写。
  2. TCC的空回滚和悬挂如何产生与解决?
    • 空回滚:Try未执行,Cancel先执行。解决方案:在Cancel逻辑中检查Try是否已执行(如查业务记录),若未执行则记录一条空回滚日志并直接返回成功。
    • 悬挂:空回滚后,Try请求才到达。解决方案:在Try逻辑开始时,检查是否存在该业务键的空回滚记录,若存在则抛出异常或直接返回,避免业务悬挂。
  3. Seata性能瓶颈主要在哪?
    • 全局锁竞争:在高并发更新同一热点数据时。
    • 网络通信开销:TC与众多RM/TM之间的RPC通信。
    • Undo Log的I/O操作:尤其是在AT模式下,每次数据更新都会产生并持久化镜像数据。



上一篇:内存涨价对MCU供应链的影响与市场分析
下一篇:Java高频面试精讲:Sentinel与Hystrix在微服务架构下的深度对比与选型指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 08:37 , Processed in 0.112685 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表