一课学透 分布式事务框架 Alibaba Seata【超清完结】

56 阅读5分钟

Seata分布式事务实战:从原理到企业级应用

本文将深入解析阿里巴巴Seata分布式事务框架的核心原理,并通过完整的代码示例展示如何在微服务架构中实现高效的事务管理,涵盖AT、TCC、SAGA和XA四种模式的实际应用场景。

一、Seata核心架构与部署

1. Seata服务端部署

# 下载Seata Server (1.5.0版本)
wget https://github.com/seata/seata/releases/download/v1.5.0/seata-server-1.5.0.tar.gz
tar -xzvf seata-server-1.5.0.tar.gz
cd seata/bin

# 修改配置文件 conf/registry.conf
registry {
  type = "nacos"
  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
  }
}

# 启动Seata Server
sh seata-server.sh -p 8091 -h 127.0.0.1 -m file

2. 数据库准备(MySQL示例)

-- 创建全局事务表
CREATE TABLE `global_table` (
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `status` TINYINT NOT NULL,
  `application_id` VARCHAR(32),
  `transaction_service_group` VARCHAR(32),
  `transaction_name` VARCHAR(128),
  `timeout` INT,
  `begin_time` BIGINT,
  `application_data` VARCHAR(2000),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
  KEY `idx_transaction_id` (`transaction_id`)
);

-- 创建分支事务表
CREATE TABLE `branch_table` (
  `branch_id` BIGINT NOT NULL,
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT,
  `resource_group_id` VARCHAR(32),
  `resource_id` VARCHAR(256),
  `branch_type` VARCHAR(8),
  `status` TINYINT,
  `client_id` VARCHAR(64),
  `application_data` VARCHAR(2000),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
);

-- 创建锁表
CREATE TABLE `lock_table` (
  `row_key` VARCHAR(128) NOT NULL,
  `xid` VARCHAR(96),
  `transaction_id` LONG,
  `branch_id` LONG,
  `resource_id` VARCHAR(256),
  `table_name` VARCHAR(32),
  `pk` VARCHAR(36),
  `gmt_create` DATETIME,
  `gmt_modified` DATETIME,
  PRIMARY KEY (`row_key`)
);

二、AT模式实战

1. Spring Cloud集成配置

# application.yml 配置
spring:
  application:
    name: order-service
  cloud:
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
  datasource:
    url: jdbc:mysql://localhost:3306/seata_order?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: ${spring.cloud.alibaba.seata.tx-service-group}
  service:
    vgroup-mapping:
      my_test_tx_group: default
    grouplist:
      default: 127.0.0.1:8091
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ""
      cluster: default

2. 业务代码示例

// OrderServiceImpl.java
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private AccountFeignClient accountFeignClient;
    
    @Autowired
    private StorageFeignClient storageFeignClient;

    @Override
    @GlobalTransactional(name = "create-order", timeoutMills = 300000, rollbackFor = Exception.class)
    public void create(Order order) {
        // 1. 创建订单
        orderMapper.insert(order);
        
        // 2. 扣减库存(调用库存服务)
        storageFeignClient.deduct(order.getCommodityCode(), order.getCount());
        
        // 3. 扣减账户余额(调用账户服务)
        accountFeignClient.debit(order.getUserId(), order.getMoney());
        
        // 4. 更新订单状态
        order.setStatus(1);
        orderMapper.updateById(order);
    }
}

// StorageServiceImpl.java
@Service
public class StorageServiceImpl implements StorageService {

    @Autowired
    private StorageMapper storageMapper;

    @Override
    @Transactional
    public void deduct(String commodityCode, int count) {
        storageMapper.deduct(commodityCode, count);
    }
}

// AccountServiceImpl.java
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    @Transactional
    public void debit(String userId, BigDecimal money) {
        accountMapper.debit(userId, money);
    }
}

3. MyBatis数据源代理配置

@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSource dataSource(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        return factoryBean.getObject();
    }
}

三、TCC模式实现

1. TCC接口定义

// 账户服务TCC接口
@LocalTCC
public interface AccountTccService {

    @TwoPhaseBusinessAction(name = "accountTccService", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepare(BusinessActionContext actionContext,
                   @BusinessActionContextParameter(paramName = "userId") String userId,
                   @BusinessActionContextParameter(paramName = "money") BigDecimal money);

    boolean commit(BusinessActionContext actionContext);

    boolean rollback(BusinessActionContext actionContext);
}

// 库存服务TCC接口
@LocalTCC
public interface StorageTccService {

    @TwoPhaseBusinessAction(name = "storageTccService", commitMethod = "commit", rollbackMethod = "rollback")
    boolean prepare(BusinessActionContext actionContext,
                   @BusinessActionContextParameter(paramName = "commodityCode") String commodityCode,
                   @BusinessActionContextParameter(paramName = "count") int count);

    boolean commit(BusinessActionContext actionContext);

    boolean rollback(BusinessActionContext actionContext);
}

2. TCC实现类

@Service
public class AccountTccServiceImpl implements AccountTccService {

    @Autowired
    private AccountMapper accountMapper;
    
    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    @Transactional
    public boolean prepare(BusinessActionContext actionContext, String userId, BigDecimal money) {
        // 检查账户余额
        Account account = accountMapper.selectById(userId);
        if (account.getBalance().compareTo(money) < 0) {
            throw new RuntimeException("账户余额不足");
        }
        
        // 冻结金额
        AccountFreeze freeze = new AccountFreeze();
        freeze.setXid(actionContext.getXid());
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freezeMapper.insert(freeze);
        
        // 扣减可用余额
        accountMapper.freeze(userId, money);
        return true;
    }

    @Override
    @Transactional
    public boolean commit(BusinessActionContext actionContext) {
        // 获取冻结记录
        AccountFreeze freeze = freezeMapper.selectById(actionContext.getXid());
        if (freeze == null || freeze.getState() != AccountFreeze.State.TRY) {
            return true;
        }
        
        // 扣减实际余额
        accountMapper.debit(freeze.getUserId(), freeze.getFreezeMoney());
        
        // 更新冻结记录状态
        freeze.setState(AccountFreeze.State.CONFIRM);
        freezeMapper.updateById(freeze);
        return true;
    }

    @Override
    @Transactional
    public boolean rollback(BusinessActionContext actionContext) {
        // 获取冻结记录
        AccountFreeze freeze = freezeMapper.selectById(actionContext.getXid());
        if (freeze == null || freeze.getState() != AccountFreeze.State.TRY) {
            return true;
        }
        
        // 释放冻结金额
        accountMapper.unfreeze(freeze.getUserId(), freeze.getFreezeMoney());
        
        // 删除冻结记录
        freezeMapper.deleteById(freeze.getXid());
        return true;
    }
}

四、SAGA模式实现

1. 状态机定义(JSON配置)

{
  "Name": "orderSaga",
  "Comment": "订单创建SAGA流程",
  "StartState": "CreateOrder",
  "Version": "1.0",
  "States": {
    "CreateOrder": {
      "Comment": "创建订单",
      "ServiceName": "orderService",
      "ServiceMethod": "create",
      "CompensateServiceName": "orderService",
      "CompensateMethod": "cancel",
      "Next": "ReduceInventory",
      "Input": [
        "$.[userId, commodityCode, count, money]"
      ],
      "Output": {
        "orderId": "$.id"
      },
      "Status": {
        "#root == true": "SU",
        "#root == false": "FA"
      }
    },
    "ReduceInventory": {
      "Comment": "扣减库存",
      "ServiceName": "storageService",
      "ServiceMethod": "deduct",
      "CompensateServiceName": "storageService",
      "CompensateMethod": "compensateDeduct",
      "Next": "DebitBalance",
      "Input": [
        "$.[commodityCode, count]",
        "$.[orderId]"
      ],
      "Output": {},
      "Status": {
        "#root == true": "SU",
        "#root == false": "FA"
      }
    },
    "DebitBalance": {
      "Comment": "扣减余额",
      "ServiceName": "accountService",
      "ServiceMethod": "debit",
      "CompensateServiceName": "accountService",
      "CompensateMethod": "compensateDebit",
      "Next": "Success",
      "Input": [
        "$.[userId, money]",
        "$.[orderId]"
      ],
      "Output": {},
      "Status": {
        "#root == true": "SU",
        "#root == false": "FA"
      }
    },
    "Success": {
      "Type": "SUCCESS"
    },
    "Fail": {
      "Type": "FAIL"
    }
  }
}

2. SAGA执行服务

@Service
public class OrderSagaService {

    @Autowired
    private StateMachineEngine stateMachineEngine;
    
    public boolean createOrder(Order order) {
        Map<String, Object> params = new HashMap<>();
        params.put("userId", order.getUserId());
        params.put("commodityCode", order.getCommodityCode());
        params.put("count", order.getCount());
        params.put("money", order.getMoney());
        
        StateMachineInstance instance = stateMachineEngine.startWithBusinessKey(
            "orderSaga", 
            "order_" + System.currentTimeMillis(),
            params
        );
        
        return instance.getStatus() == StateMachineInstance.SUCCESS;
    }
}

五、XA模式实现

1. 数据源配置

# application.yml 配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/seata_order?useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    hikari:
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      maximum-pool-size: 20
      minimum-idle: 10

seata:
  data-source-proxy-mode: XA

2. 业务代码示例

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private AccountMapper accountMapper;
    
    @Autowired
    private StorageMapper storageMapper;

    @Override
    @GlobalTransactional(name = "xa-create-order", timeoutMills = 300000)
    public void create(Order order) {
        // 1. 创建订单
        orderMapper.insert(order);
        
        // 2. 扣减库存
        storageMapper.deduct(order.getCommodityCode(), order.getCount());
        
        // 3. 扣减账户余额
        accountMapper.debit(order.getUserId(), order.getMoney());
        
        // 4. 更新订单状态
        order.setStatus(1);
        orderMapper.updateById(order);
    }
}

六、Seata高级配置

1. 客户端配置优化

# client.properties
# 事务日志存储模式
client.log.store.mode=db

# 数据库存储配置
client.store.db.datasource=druid
client.store.db.db-type=mysql
client.store.db.driver-class-name=com.mysql.jdbc.Driver
client.store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false
client.store.db.user=root
client.store.db.password=123456
client.store.db.min-conn=5
client.store.db.max-conn=30

# 全局事务超时时间(毫秒)
client.tm.degrade-check=false
client.tm.degrade-check-allow-times=10
client.tm.degrade-check-period=2000
client.tm.commit-retry-count=5
client.tm.rollback-retry-count=5

# 分支事务配置
client.rm.report.retry.count=5
client.rm.async.commit.buffer.limit=10000
client.rm.lock.retry.internal=10
client.rm.lock.retry.times=30

2. 服务端高可用配置

# seata-server配置 file.conf
store {
  mode = "db"
  
  db {
    datasource = "druid"
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata_server?useSSL=false"
    user = "root"
    password = "123456"
    min-conn = 5
    max-conn = 100
    global.table = "global_table"
    branch.table = "branch_table"
    lock.table = "lock_table"
    query-limit = 100
  }
}

service {
  vgroup_mapping.my_test_tx_group = "default"
  
  default.grouplist = "127.0.0.1:8091,127.0.0.1:8092,127.0.0.1:8093"
  
  enable-degrade = false
  disable-global-transaction = false
}

transport {
  shutdown.wait = "3s"
  thread-factory.boss-thread-prefix = "NettyBoss"
  thread-factory.worker-thread-prefix = "NettyServerNIOWorker"
  thread-factory.server-executor-thread-prefix = "NettyServerBizHandler"
  thread-factory.share-boss-worker = false
  thread-factory.client-selector-thread-prefix = "NettyClientSelector"
  thread-factory.client-selector-thread-size = 1
  thread-factory.client-worker-thread-prefix = "NettyClientWorkerThread"
  thread-factory.boss-thread-size = 1
  thread-factory.worker-thread-size = "default"
  type = "TCP"
  server = "NIO"
  heartbeat = true
  serialization = "seata"
  compressor = "none"
}

七、监控与故障排查

1. Seata控制台集成

# seata控制台配置
server:
  port: 7091

spring:
  application:
    name: seata-console
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/seata_console?useSSL=false
    username: root
    password: 123456

seata:
  console:
    enabled: true
    user:
      username: admin
      password: seata123

2. Metrics监控集成

@Configuration
public class SeataMetricsConfig {

    @Bean
    public MeterRegistryCustomizer<MeterRegistry> seataMetricsCommonTags() {
        return registry -> registry.config().commonTags(
            "application", "order-service",
            "seata.server", "127.0.0.1:8091"
        );
    }

    @Bean
    public SeataMeterRegistry seataMeterRegistry() {
        return new SeataMeterRegistry(
            new SeataMeterRegistryConfig() {
                @Override
                public String get(String key) {
                    return null;
                }
                
                @Override
                public String prefix() {
                    return "seata.metrics";
                }
            },
            Clock.SYSTEM
        );
    }
}

八、最佳实践与性能优化

1. 事务分组规划

# 生产环境推荐的事务分组配置
# 按业务域划分
vgroup_mapping.order-service-group=cluster-a
vgroup_mapping.account-service-group=cluster-b
vgroup_mapping.storage-service-group=cluster-c

# 按环境划分
vgroup_mapping.order-service-dev-group=dev-cluster
vgroup_mapping.order-service-test-group=test-cluster