跳转至

第三章:AT 模式

AT 模式原理

工作流程

AT 模式工作流程:

一阶段:
1. 解析 SQL
2. 记录 undo_log(修改前数据)
3. 执行 SQL
4. 记录 undo_log(修改后数据)
5. 获取全局锁
6. 提交本地事务

二阶段(提交):
1. 异步删除 undo_log

二阶段(回滚):
1. 根据 undo_log 回滚数据
2. 释放全局锁

示例

-- 原始数据
UPDATE account SET balance = balance - 100 WHERE id = 1;

-- undo_log 记录
{
  "beforeImage": {
    "rows": [{"id": 1, "balance": 1000}]
  },
  "afterImage": {
    "rows": [{"id": 1, "balance": 900}]
  }
}

-- 回滚时执行
UPDATE account SET balance = 1000 WHERE id = 1;

快速开始

服务端部署

# 下载 Seata Server
wget https://github.com/seata/seata/releases/download/v1.7.0/seata-server-1.7.0.tar.gz
tar -xzf seata-server-1.7.0.tar.gz
cd seata

# 启动
sh bin/seata-server.sh

客户端配置

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.7.0</version>
</dependency>
# application.yml
seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_tx_group
  service:
    vgroup-mapping:
      my_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: public
      group: SEATA_GROUP

创建 undo_log 表

-- 每个数据库都需要创建
CREATE TABLE `undo_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `branch_id` bigint NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB;

使用示例

开启事务

import io.seata.spring.annotation.GlobalTransactional;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private AccountService accountService;

    @Autowired
    private StorageService storageService;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(Order order) {
        // 创建订单
        orderMapper.insert(order);

        // 扣减余额
        accountService.decreaseBalance(order.getUserId(), order.getAmount());

        // 扣减库存
        storageService.decreaseStock(order.getProductId(), order.getCount());
    }
}

分支事务

@Service
public class AccountService {

    @Autowired
    private AccountMapper accountMapper;

    public void decreaseBalance(Long userId, BigDecimal amount) {
        Account account = accountMapper.selectByUserId(userId);
        if (account.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足");
        }

        accountMapper.decreaseBalance(userId, amount);
    }
}

配置详解

注册中心配置

seata:
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: public
      group: SEATA_GROUP
      application: seata-server

配置中心配置

seata:
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: public
      group: SEATA_GROUP

数据源代理

import io.seata.rm.datasource.DataSourceProxy;

@Configuration
public class DataSourceConfig {

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

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

注意事项

1. SQL 支持

支持的 SQL:
- INSERT
- UPDATE
- DELETE
- SELECT FOR UPDATE

不支持的 SQL:
- 多表关联更新
- 存储过程
- 触发器

2. 主键要求

主键要求:
- 表必须有主键
- 主键不能为联合主键

3. 字段类型

支持的字段类型:
- 数值类型
- 字符串类型
- 日期类型
- 其他常用类型

不支持的字段类型:
- BLOB
- CLOB

小结

AT 模式要点:

  • 原理:一阶段记录 undo_log,二阶段提交/回滚
  • 快速开始:服务端部署、客户端配置
  • 使用示例:@GlobalTransactional
  • 配置详解:注册中心、配置中心、数据源代理
  • 注意事项:SQL 支持、主键要求、字段类型

下一章我们将学习 TCC 模式。