一文带你读懂事务(Transaction)

事务全面指南

目录


事务简介

什么是事务

事务(Transaction) 是数据库管理系统中的一个逻辑操作单元,它包含一组操作,这些操作要么全部成功执行,要么全部失败回滚,保证数据的一致性和完整性。

事务的ACID特性

1. 原子性(Atomicity)
  • 事务中的所有操作要么全部成功,要么全部失败
  • 不存在部分成功、部分失败的情况
  • 如果事务执行过程中出现错误,所有已执行的操作都会回滚
@Transactional
public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
    // 扣款
    accountService.debit(fromAccount, amount);
  
    // 如果这里抛出异常,扣款操作也会回滚
    if (amount.compareTo(BigDecimal.ZERO) <= 0) {
        throw new IllegalArgumentException("转账金额必须大于0");
    }
  
    // 加款
    accountService.credit(toAccount, amount);
}
2. 一致性(Consistency)
  • 事务执行前后,数据库从一个一致状态转换到另一个一致状态
  • 所有约束条件、触发器等都必须得到满足
  • 数据完整性不会被破坏
@Transactional
public void createOrder(Order order) {
    // 检查库存
    if (!inventoryService.checkStock(order.getProductId(), order.getQuantity())) {
        throw new InsufficientStockException("库存不足");
    }
  
    // 创建订单
    orderRepository.save(order);
  
    // 减少库存
    inventoryService.reduceStock(order.getProductId(), order.getQuantity());
  
    // 事务结束后,订单和库存数据保持一致
}
3. 隔离性(Isolation)
  • 多个事务并发执行时,事务之间相互隔离
  • 一个事务的执行不应该被其他事务干扰
  • 通过不同的隔离级别来实现不同程度的隔离
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processOrder(Long orderId) {
    // 读取订单信息
    Order order = orderRepository.findById(orderId);
  
    // 其他事务的修改不会影响这里的读取
    // 除非它们已经提交
  
    // 处理订单逻辑
    processOrderLogic(order);
}
4. 持久性(Durability)
  • 一旦事务提交,其所做的修改就会永久保存在数据库中
  • 即使系统崩溃,提交的事务也不会丢失
  • 通过日志和备份机制来保证
@Transactional
public void saveUser(User user) {
    // 保存用户信息
    userRepository.save(user);
  
    // 事务提交后,数据永久保存
    // 即使系统重启,数据也不会丢失
}

事务的生命周期

基础事务生命周期流程图
开始事务
分配事务ID
记录事务开始日志
获取数据库连接
设置事务隔离级别
执行SQL操作
操作是否成功?
继续执行下一个操作
记录错误日志
还有操作需要执行?
是否提交事务?
回滚事务
提交事务
释放数据库连接
释放数据库连接
事务结束
清理事务资源
详细事务状态转换图
开始事务
执行SQL
操作完成
操作失败
提交事务
回滚事务
提交成功
回滚完成
释放资源
释放资源
空闲状态
活动状态
执行状态
回滚状态
提交状态
已提交
已回滚
Spring事务生命周期简化流程图
方法调用
事务拦截
开始事务
执行业务
成功?
提交
回滚
返回结果
Spring事务生命周期详细说明
阶段描述关键操作技术细节
方法调用客户端调用业务方法方法参数验证、权限检查通过Spring AOP拦截
注解检查检查方法是否有事务注解解析@Transactional属性使用反射获取注解信息
代理创建创建事务代理对象生成代理类、设置拦截器基于CGLIB或JDK动态代理
事务管理器获取事务管理器实例根据数据源选择管理器DataSourceTransactionManager等
事务定义创建事务定义对象设置传播特性、隔离级别DefaultTransactionDefinition
开始事务开始数据库事务获取数据库连接、设置自动提交调用Connection.setAutoCommit(false)
属性设置设置事务属性隔离级别、超时时间、只读标志应用事务定义中的配置
执行业务执行实际的业务逻辑SQL执行、数据修改在事务上下文中执行
结果判断判断业务执行结果异常检测、成功确认捕获RuntimeException和Error
事务提交提交数据库事务刷新日志、释放锁调用Connection.commit()
事务回滚回滚数据库事务撤销修改、释放锁调用Connection.rollback()
资源释放释放事务相关资源关闭连接、清理缓存确保资源不泄漏
返回结果返回方法执行结果异常传播、结果封装保持方法签名一致
数据库事务内部流程
事务开始
写前日志WAL
修改内存数据
写后日志WAL
是否提交?
刷新日志到磁盘
丢弃内存修改
事务提交完成
事务回滚完成
事务并发控制流程图
事务A开始
获取锁
锁是否可用?
加锁成功
等待锁释放
执行操作
释放锁
等待超时?
抛出超时异常
事务A完成
事务A失败
事务超时处理流程图
事务开始
记录开始时间
执行操作
检查是否超时?
操作完成?
强制回滚事务
提交事务
记录超时日志
事务成功完成
事务超时失败
分布式事务生命周期
全局事务开始
协调者创建全局事务
参与者1开始本地事务
参与者2开始本地事务
参与者1执行操作
参与者2执行操作
操作成功?
操作成功?
参与者1准备就绪
参与者1回滚
参与者2准备就绪
参与者2回滚
等待协调者决定
全局事务回滚
协调者决定提交?
所有参与者提交
所有参与者回滚
全局事务成功
全局事务失败
事务生命周期各阶段说明
阶段描述关键操作注意事项
开始阶段事务初始化分配事务ID、设置隔离级别、获取连接确保资源可用
执行阶段执行业务逻辑SQL执行、数据修改、日志记录异常处理和回滚准备
提交阶段事务确认日志刷新、数据持久化、释放锁确保数据一致性
回滚阶段事务撤销撤销修改、释放锁、清理日志快速回滚避免阻塞
结束阶段资源清理释放连接、清理缓存、更新统计避免资源泄漏
事务生命周期监控点
@Component
public class TransactionLifecycleMonitor {
  
    @EventListener
    public void handleTransactionStarted(TransactionStartedEvent event) {
        log.info("事务开始: ID={}, 隔离级别={}, 超时={}", 
            event.getTransactionId(), 
            event.getIsolationLevel(), 
            event.getTimeout());
    }
  
    @EventListener
    public void handleTransactionCommitted(TransactionCommittedEvent event) {
        log.info("事务提交: ID={}, 执行时间={}ms", 
            event.getTransactionId(), 
            event.getExecutionTime());
    }
  
    @EventListener
    public void handleTransactionRolledBack(TransactionRolledBackEvent event) {
        log.warn("事务回滚: ID={}, 原因={}", 
            event.getTransactionId(), 
            event.getRollbackReason());
    }
}

事务隔离级别

事务隔离级别概述

事务隔离级别定义了事务之间相互影响的程度,从低到高依次为:读未提交读已提交可重复读串行化

1. 读未提交(Read Uncommitted)

特点
  • 最低的隔离级别
  • 一个事务可以读取另一个未提交事务的数据
  • 存在脏读、不可重复读、幻读问题
示例
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void processWithReadUncommitted() {
    // 事务A读取数据
    User user = userRepository.findById(1L);
    System.out.println("读取到的用户: " + user.getName()); // 可能读取到未提交的数据
  
    // 其他事务B可能正在修改这条数据但还未提交
    // 如果B回滚,这里读取的就是脏数据
}
问题演示
// 事务A
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void transactionA() {
    User user = userRepository.findById(1L);
    System.out.println("A读取: " + user.getName()); // 输出: 张三
  
    // 等待事务B执行
    Thread.sleep(1000);
  
    user = userRepository.findById(1L);
    System.out.println("A再次读取: " + user.getName()); // 可能输出: 李四(脏读)
}

// 事务B
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void transactionB() {
    User user = userRepository.findById(1L);
    user.setName("李四");
    userRepository.save(user);
  
    // 模拟异常,事务回滚
    throw new RuntimeException("模拟异常");
}

2. 读已提交(Read Committed)

特点
  • 一个事务只能读取另一个已提交事务的数据
  • 解决了脏读问题
  • 仍存在不可重复读、幻读问题
示例
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processWithReadCommitted() {
    // 只能读取已提交的数据
    User user = userRepository.findById(1L);
    System.out.println("读取到的用户: " + user.getName());
  
    // 其他事务的修改只有在提交后才能被读取
    // 避免了脏读问题
}
问题演示
// 事务A
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transactionA() {
    User user = userRepository.findById(1L);
    System.out.println("A第一次读取: " + user.getName()); // 输出: 张三
  
    // 等待事务B提交
    Thread.sleep(1000);
  
    user = userRepository.findById(1L);
    System.out.println("A第二次读取: " + user.getName()); // 可能输出: 李四(不可重复读)
}

// 事务B
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transactionB() {
    User user = userRepository.findById(1L);
    user.setName("李四");
    userRepository.save(user);
    // 事务提交
}

3. 可重复读(Repeatable Read)

特点
  • 一个事务执行过程中,多次读取同一数据得到相同结果
  • 解决了脏读、不可重复读问题
  • 仍存在幻读问题
示例
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processWithRepeatableRead() {
    // 第一次读取
    User user = userRepository.findById(1L);
    System.out.println("第一次读取: " + user.getName());
  
    // 即使其他事务修改了这条数据,这里读取的结果仍然相同
    user = userRepository.findById(1L);
    System.out.println("第二次读取: " + user.getName()); // 结果相同
  
    // 但是可能读取到新插入的数据(幻读)
}
问题演示
// 事务A
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void transactionA() {
    List<User> users = userRepository.findAll();
    System.out.println("A第一次读取用户数量: " + users.size()); // 输出: 10
  
    // 等待事务B插入新用户
    Thread.sleep(1000);
  
    users = userRepository.findAll();
    System.out.println("A第二次读取用户数量: " + users.size()); // 可能输出: 11(幻读)
}

// 事务B
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void transactionB() {
    User newUser = new User();
    newUser.setName("新用户");
    userRepository.save(newUser);
    // 事务提交
}

4. 串行化(Serializable)

特点
  • 最高的隔离级别
  • 事务串行执行,完全隔离
  • 解决了所有并发问题
  • 性能最低,并发性最差
示例
@Transactional(isolation = Isolation.SERIALIZABLE)
public void processWithSerializable() {
    // 事务完全串行执行
    // 不会出现任何并发问题
    // 但性能较低
  
    User user = userRepository.findById(1L);
    user.setName("新名称");
    userRepository.save(user);
}

隔离级别对比表

隔离级别脏读不可重复读幻读性能并发性
读未提交最高最高
读已提交
可重复读
串行化最低最低

事务传播特性

事务传播特性概述

事务传播特性定义了当一个事务方法被另一个事务方法调用时,事务应该如何传播。

1. REQUIRED(默认)

特点
  • 如果当前存在事务,则加入该事务
  • 如果当前不存在事务,则创建一个新事务
示例
@Service
public class OrderService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 如果没有事务,创建新事务
        // 如果已有事务,加入现有事务
      
        orderRepository.save(order);
      
        // 调用库存服务
        inventoryService.reduceStock(order.getProductId(), order.getQuantity());
    }
}

@Service
public class InventoryService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void reduceStock(Long productId, Integer quantity) {
        // 加入OrderService的事务
        // 如果这里抛出异常,整个事务都会回滚
      
        Inventory inventory = inventoryRepository.findByProductId(productId);
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
    }
}
使用场景
  • 订单创建时同时减少库存
  • 用户注册时同时创建用户档案
  • 支付时同时更新订单状态和账户余额

2. REQUIRES_NEW

特点
  • 总是创建一个新事务
  • 如果当前存在事务,则挂起当前事务
示例
@Service
public class OrderService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 主事务
        orderRepository.save(order);
      
        // 调用日志服务,创建新事务
        logService.recordOrderLog(order);
      
        // 即使日志记录失败,订单创建仍然成功
    }
}

@Service
public class LogService {
  
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void recordOrderLog(Order order) {
        // 新事务,独立于OrderService的事务
      
        OrderLog log = new OrderLog();
        log.setOrderId(order.getId());
        log.setAction("CREATE");
        log.setTimestamp(LocalDateTime.now());
      
        logRepository.save(log);
      
        // 如果这里抛出异常,不会影响订单创建
    }
}
使用场景
  • 记录操作日志
  • 发送通知消息
  • 更新统计数据

3. SUPPORTS

特点
  • 如果当前存在事务,则加入该事务
  • 如果当前不存在事务,则以非事务方式执行
示例
@Service
public class UserService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createUser(User user) {
        userRepository.save(user);
      
        // 调用查询服务,支持事务但不强制
        userQueryService.updateUserCache(user.getId());
    }
}

@Service
public class UserQueryService {
  
    @Transactional(propagation = Propagation.SUPPORTS)
    public void updateUserCache(Long userId) {
        // 如果有事务,加入事务
        // 如果没有事务,非事务执行
      
        User user = userRepository.findById(userId);
        cacheService.put("user:" + userId, user);
    }
}
使用场景
  • 缓存更新
  • 数据查询
  • 非关键业务逻辑

4. NOT_SUPPORTED

特点
  • 以非事务方式执行
  • 如果当前存在事务,则挂起当前事务
示例
@Service
public class OrderService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 主事务
        orderRepository.save(order);
      
        // 调用外部API,挂起事务
        externalApiService.notifyOrderCreated(order);
      
        // 即使外部API调用失败,订单创建仍然成功
    }
}

@Service
public class ExternalApiService {
  
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void notifyOrderCreated(Order order) {
        // 非事务执行,挂起OrderService的事务
      
        try {
            // 调用外部API
            restTemplate.postForEntity("http://external-api/notify", order, String.class);
        } catch (Exception e) {
            // 外部API调用失败不影响主事务
            log.error("外部API调用失败", e);
        }
    }
}
使用场景
  • 调用外部服务
  • 发送邮件/短信
  • 文件上传下载

5. MANDATORY

特点
  • 必须在事务中执行
  • 如果当前不存在事务,则抛出异常
示例
@Service
public class OrderService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 主事务
        orderRepository.save(order);
      
        // 调用库存服务,必须在事务中
        inventoryService.reduceStock(order.getProductId(), order.getQuantity());
    }
}

@Service
public class InventoryService {
  
    @Transactional(propagation = Propagation.MANDATORY)
    public void reduceStock(Long productId, Integer quantity) {
        // 必须在事务中执行
        // 如果没有事务,抛出异常
      
        Inventory inventory = inventoryRepository.findByProductId(productId);
        if (inventory.getStock() < quantity) {
            throw new InsufficientStockException("库存不足");
        }
      
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
    }
}
使用场景
  • 核心业务逻辑
  • 数据一致性要求高的操作
  • 必须与调用方在同一事务中的操作

6. NEVER

特点
  • 不能在事务中执行
  • 如果当前存在事务,则抛出异常
示例
@Service
public class OrderService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 主事务
        orderRepository.save(order);
      
        // 调用统计服务,不能在事务中
        try {
            statisticsService.updateOrderCount();
        } catch (Exception e) {
            // 统计更新失败不影响订单创建
            log.error("统计更新失败", e);
        }
    }
}

@Service
public class StatisticsService {
  
    @Transactional(propagation = Propagation.NEVER)
    public void updateOrderCount() {
        // 不能在事务中执行
        // 如果被事务方法调用,抛出异常
      
        Statistics stats = statisticsRepository.findByType("ORDER_COUNT");
        stats.setValue(stats.getValue() + 1);
        statisticsRepository.save(stats);
    }
}
使用场景
  • 统计计算
  • 报表生成
  • 独立的数据处理

7. NESTED

特点
  • 如果当前存在事务,则创建一个嵌套事务
  • 如果当前不存在事务,则等同于REQUIRED
示例
@Service
public class OrderService {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 主事务
        orderRepository.save(order);
      
        // 调用库存服务,创建嵌套事务
        try {
            inventoryService.reduceStock(order.getProductId(), order.getQuantity());
        } catch (Exception e) {
            // 库存操作失败不影响订单创建
            log.error("库存操作失败", e);
        }
    }
}

@Service
public class InventoryService {
  
    @Transactional(propagation = Propagation.NESTED)
    public void reduceStock(Long productId, Integer quantity) {
        // 嵌套事务
        // 如果失败,只回滚嵌套事务,不影响主事务
      
        Inventory inventory = inventoryRepository.findByProductId(productId);
        if (inventory.getStock() < quantity) {
            throw new InsufficientStockException("库存不足");
        }
      
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
    }
}
使用场景
  • 可选的操作步骤
  • 不影响主业务流程的辅助操作
  • 需要部分回滚的场景

传播特性对比表

传播特性当前事务新事务异常影响使用场景
REQUIRED加入创建全部回滚默认选择
REQUIRES_NEW挂起创建独立回滚独立操作
SUPPORTS加入非事务部分回滚可选操作
NOT_SUPPORTED挂起非事务无影响外部调用
MANDATORY加入异常全部回滚核心业务
NEVER异常非事务无影响统计计算
NESTED嵌套创建部分回滚可选步骤

事务失效方式

事务失效概述

事务失效是指事务注解没有按预期工作,导致事务无法正常提交或回滚。

1. 方法访问权限问题

问题描述
  • 私有方法(private)上的@Transactional注解无效
  • 受保护方法(protected)上的@Transactional注解无效
  • 只有public方法上的@Transactional注解才有效
示例
@Service
public class UserService {
  
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
      
        // 调用私有方法,事务注解无效
        updateUserCache(user);
    }
  
    @Transactional // ❌ 无效,私有方法
    private void updateUserCache(User user) {
        cacheService.put("user:" + user.getId(), user);
        // 如果这里抛出异常,不会回滚
    }
}
解决方案
@Service
public class UserService {
  
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
      
        // 将私有方法改为公共方法
        updateUserCache(user);
    }
  
    @Transactional // ✅ 有效,公共方法
    public void updateUserCache(User user) {
        cacheService.put("user:" + user.getId(), user);
        // 如果这里抛出异常,会回滚
    }
}

2. 方法自调用问题

问题描述
  • 同一个类中的方法直接调用,事务注解无效
  • Spring的事务管理基于AOP代理,自调用无法触发代理
示例
@Service
public class OrderService {
  
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
      
        // 自调用,事务注解无效
        this.updateInventory(order.getProductId(), order.getQuantity());
    }
  
    @Transactional // ❌ 无效,自调用
    public void updateInventory(Long productId, Integer quantity) {
        Inventory inventory = inventoryRepository.findByProductId(productId);
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
      
        // 如果这里抛出异常,不会回滚
    }
}
解决方案1:注入自身
@Service
public class OrderService {
  
    @Autowired
    private OrderService self; // 注入自身
  
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
      
        // 通过注入的自身调用,触发代理
        self.updateInventory(order.getProductId(), order.getQuantity());
    }
  
    @Transactional // ✅ 有效,通过代理调用
    public void updateInventory(Long productId, Integer quantity) {
        Inventory inventory = inventoryRepository.findByProductId(productId);
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
      
        // 如果这里抛出异常,会回滚
    }
}
解决方案2:使用AopContext
@Service
public class OrderService {
  
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
      
        // 通过AopContext获取代理对象
        OrderService proxy = (OrderService) AopContext.currentProxy();
        proxy.updateInventory(order.getProductId(), order.getQuantity());
    }
  
    @Transactional // ✅ 有效,通过代理调用
    public void updateInventory(Long productId, Integer quantity) {
        Inventory inventory = inventoryRepository.findByProductId(productId);
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
    }
}
解决方案3:重构方法结构
@Service
public class OrderService {
  
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        updateInventory(order.getProductId(), order.getQuantity());
    }
  
    // 移除事务注解,在主事务中执行
    private void updateInventory(Long productId, Integer quantity) {
        Inventory inventory = inventoryRepository.findByProductId(productId);
        inventory.setStock(inventory.getStock() - quantity);
        inventoryRepository.save(inventory);
    }
}

3. 异常类型问题

问题描述
  • 默认情况下,只有RuntimeException和Error会触发回滚
  • 受检异常(Checked Exception)不会触发回滚
  • 异常被捕获后不会触发回滚
示例
@Service
public class UserService {
  
    @Transactional
    public void createUser(User user) throws Exception {
        userRepository.save(user);
      
        // 抛出受检异常,不会回滚
        if (user.getAge() < 0) {
            throw new Exception("年龄不能为负数"); // ❌ 不会回滚
        }
      
        // 抛出运行时异常,会回滚
        if (user.getName() == null) {
            throw new RuntimeException("姓名不能为空"); // ✅ 会回滚
        }
    }
}
解决方案1:配置回滚异常
@Service
public class UserService {
  
    @Transactional(rollbackFor = Exception.class) // 所有异常都回滚
    public void createUser(User user) throws Exception {
        userRepository.save(user);
      
        if (user.getAge() < 0) {
            throw new Exception("年龄不能为负数"); // ✅ 会回滚
        }
    }
}
解决方案2:转换为运行时异常
@Service
public class UserService {
  
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
      
        if (user.getAge() < 0) {
            // 转换为运行时异常
            throw new RuntimeException("年龄不能为负数", new Exception("原始异常"));
        }
    }
}
解决方案3:不捕获异常
@Service
public class UserService {
  
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
      
        try {
            validateUser(user);
        } catch (Exception e) {
            // ❌ 捕获异常后不会回滚
            log.error("用户验证失败", e);
            // 应该重新抛出异常
            throw new RuntimeException("用户验证失败", e);
        }
    }
  
    private void validateUser(User user) throws Exception {
        if (user.getAge() < 0) {
            throw new Exception("年龄不能为负数");
        }
    }
}

4. 数据库引擎不支持

问题描述
  • MyISAM引擎不支持事务
  • 只有InnoDB引擎支持事务
  • 数据库连接配置问题
示例
-- MyISAM引擎,不支持事务
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    age INT
) ENGINE=MyISAM;

-- InnoDB引擎,支持事务
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    age INT
) ENGINE=InnoDB;
解决方案
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        # 强制使用InnoDB引擎
        hibernate.connection.provider_disables_autocommit: true

5. 事务方法被非事务方法调用

问题描述
  • 非事务方法调用事务方法,事务注解无效
  • 事务传播特性配置不当
示例
@Service
public class OrderService {
  
    // 非事务方法
    public void processOrder(Order order) {
        // 直接调用事务方法,事务注解无效
        createOrder(order);
        sendNotification(order);
    }
  
    @Transactional // ❌ 无效,被非事务方法调用
    public void createOrder(Order order) {
        orderRepository.save(order);
        // 如果这里抛出异常,不会回滚
    }
  
    private void sendNotification(Order order) {
        // 发送通知逻辑
    }
}
解决方案
@Service
public class OrderService {
  
    @Transactional // ✅ 将事务注解移到外层方法
    public void processOrder(Order order) {
        createOrder(order);
        sendNotification(order);
    }
  
    // 移除事务注解,在主事务中执行
    public void createOrder(Order order) {
        orderRepository.save(order);
    }
  
    private void sendNotification(Order order) {
        // 发送通知逻辑
    }
}

6. 事务超时设置

问题描述
  • 事务执行时间过长,超过超时设置
  • 默认超时时间为30秒
示例
@Service
public class DataProcessService {
  
    @Transactional(timeout = 10) // 10秒超时
    public void processLargeData() {
        // 处理大量数据,可能超过10秒
        for (int i = 0; i < 1000000; i++) {
            // 模拟耗时操作
            Thread.sleep(1);
        }
      
        // 如果超时,事务会回滚
    }
}
解决方案
@Service
public class DataProcessService {
  
    @Transactional(timeout = 300) // 增加超时时间到5分钟
    public void processLargeData() {
        // 处理大量数据
        processDataInBatches();
    }
  
    private void processDataInBatches() {
        // 分批处理,避免单次处理时间过长
        int batchSize = 1000;
        for (int i = 0; i < totalSize; i += batchSize) {
            processBatch(i, Math.min(i + batchSize, totalSize));
        }
    }
}

事务失效检查清单

检查项状态说明
方法是否为public私有和受保护方法无效
是否为自调用同一类中直接调用无效
异常类型是否正确受检异常默认不回滚
是否捕获异常捕获异常后不会回滚
数据库引擎是否支持只有InnoDB支持事务
是否被非事务方法调用外层方法需要事务注解
超时设置是否合理避免事务超时回滚

事务应用场景

事务应用场景概述

事务在业务系统中有着广泛的应用,主要用于保证数据一致性和业务完整性。

1. 金融交易场景

银行转账
@Service
public class BankTransferService {
  
    @Transactional
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // 1. 检查转出账户余额
        Account from = accountRepository.findByAccountNumber(fromAccount);
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientBalanceException("余额不足");
        }
      
        // 2. 扣减转出账户余额
        from.setBalance(from.getBalance().subtract(amount));
        accountRepository.save(from);
      
        // 3. 增加转入账户余额
        Account to = accountRepository.findByAccountNumber(toAccount);
        to.setBalance(to.getBalance().add(amount));
        accountRepository.save(to);
      
        // 4. 记录交易流水
        TransactionRecord record = new TransactionRecord();
        record.setFromAccount(fromAccount);
        record.setToAccount(toAccount);
        record.setAmount(amount);
        record.setTransactionTime(LocalDateTime.now());
        transactionRecordRepository.save(record);
      
        // 如果任何步骤失败,整个转账操作回滚
    }
}
支付处理
@Service
public class PaymentService {
  
    @Transactional
    public void processPayment(PaymentRequest request) {
        // 1. 验证支付信息
        validatePaymentRequest(request);
      
        // 2. 扣减用户余额
        User user = userRepository.findById(request.getUserId());
        user.setBalance(user.getBalance().subtract(request.getAmount()));
        userRepository.save(user);
      
        // 3. 更新订单状态
        Order order = orderRepository.findById(request.getOrderId());
        order.setStatus(OrderStatus.PAID);
        order.setPaymentTime(LocalDateTime.now());
        orderRepository.save(order);
      
        // 4. 记录支付流水
        PaymentRecord payment = new PaymentRecord();
        payment.setUserId(request.getUserId());
        payment.setOrderId(request.getOrderId());
        payment.setAmount(request.getAmount());
        payment.setPaymentMethod(request.getPaymentMethod());
        paymentRecordRepository.save(payment);
      
        // 5. 发送支付成功通知
        notificationService.sendPaymentSuccessNotification(user, order);
    }
}

2. 电商订单场景

订单创建
@Service
public class OrderService {
  
    @Transactional
    public Order createOrder(OrderRequest request) {
        // 1. 验证商品库存
        for (OrderItem item : request.getItems()) {
            Product product = productRepository.findById(item.getProductId());
            if (product.getStock() < item.getQuantity()) {
                throw new InsufficientStockException("商品库存不足: " + product.getName());
            }
        }
      
        // 2. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setStatus(OrderStatus.CREATED);
        order.setCreateTime(LocalDateTime.now());
        order.setItems(request.getItems());
        order.setTotalAmount(calculateTotalAmount(request.getItems()));
        orderRepository.save(order);
      
        // 3. 扣减库存
        for (OrderItem item : request.getItems()) {
            Product product = productRepository.findById(item.getProductId());
            product.setStock(product.getStock() - item.getQuantity());
            productRepository.save(product);
        }
      
        // 4. 创建订单项
        for (OrderItem item : request.getItems()) {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(order.getId());
            orderItem.setProductId(item.getProductId());
            orderItem.setQuantity(item.getQuantity());
            orderItem.setPrice(item.getPrice());
            orderItemRepository.save(orderItem);
        }
      
        // 5. 更新用户积分
        User user = userRepository.findById(request.getUserId());
        user.setPoints(user.getPoints() + calculatePoints(order.getTotalAmount()));
        userRepository.save(user);
      
        return order;
    }
}
订单取消
@Service
public class OrderService {
  
    @Transactional
    public void cancelOrder(Long orderId) {
        // 1. 查询订单
        Order order = orderRepository.findById(orderId);
        if (order == null) {
            throw new OrderNotFoundException("订单不存在");
        }
      
        if (order.getStatus() != OrderStatus.CREATED) {
            throw new InvalidOrderStatusException("订单状态不允许取消");
        }
      
        // 2. 更新订单状态
        order.setStatus(OrderStatus.CANCELLED);
        order.setCancelTime(LocalDateTime.now());
        orderRepository.save(order);
      
        // 3. 恢复库存
        for (OrderItem item : order.getItems()) {
            Product product = productRepository.findById(item.getProductId());
            product.setStock(product.getStock() + item.getQuantity());
            productRepository.save(product);
        }
      
        // 4. 退还积分
        User user = userRepository.findById(order.getUserId());
        user.setPoints(user.getPoints() - calculatePoints(order.getTotalAmount()));
        userRepository.save(user);
      
        // 5. 记录取消日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setAction("CANCEL");
        log.setTimestamp(LocalDateTime.now());
        orderLogRepository.save(log);
    }
}

3. 用户管理场景

用户注册
@Service
public class UserService {
  
    @Transactional
    public User registerUser(UserRegistrationRequest request) {
        // 1. 验证用户名唯一性
        if (userRepository.existsByUsername(request.getUsername())) {
            throw new UsernameAlreadyExistsException("用户名已存在");
        }
      
        // 2. 验证邮箱唯一性
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new EmailAlreadyExistsException("邮箱已存在");
        }
      
        // 3. 创建用户
        User user = new User();
        user.setUsername(request.getUsername());
        user.setEmail(request.getEmail());
        user.setPassword(encryptPassword(request.getPassword()));
        user.setStatus(UserStatus.ACTIVE);
        user.setCreateTime(LocalDateTime.now());
        userRepository.save(user);
      
        // 4. 创建用户档案
        UserProfile profile = new UserProfile();
        profile.setUserId(user.getId());
        profile.setNickname(request.getNickname());
        profile.setAvatar(request.getAvatar());
        userProfileRepository.save(profile);
      
        // 5. 创建用户角色
        UserRole userRole = new UserRole();
        userRole.setUserId(user.getId());
        userRole.setRoleId(RoleConstants.USER_ROLE_ID);
        userRoleRepository.save(userRole);
      
        // 6. 发送欢迎邮件
        emailService.sendWelcomeEmail(user.getEmail());
      
        return user;
    }
}
用户删除
@Service
public class UserService {
  
    @Transactional
    public void deleteUser(Long userId) {
        // 1. 查询用户
        User user = userRepository.findById(userId);
        if (user == null) {
            throw new UserNotFoundException("用户不存在");
        }
      
        // 2. 检查用户状态
        if (user.getStatus() == UserStatus.DELETED) {
            throw new UserAlreadyDeletedException("用户已被删除");
        }
      
        // 3. 删除用户相关数据
        userProfileRepository.deleteByUserId(userId);
        userRoleRepository.deleteByUserId(userId);
        userPermissionRepository.deleteByUserId(userId);
      
        // 4. 软删除用户
        user.setStatus(UserStatus.DELETED);
        user.setDeleteTime(LocalDateTime.now());
        userRepository.save(user);
      
        // 5. 记录删除日志
        UserLog log = new UserLog();
        log.setUserId(userId);
        log.setAction("DELETE");
        log.setTimestamp(LocalDateTime.now());
        userLogRepository.save(log);
    }
}

4. 库存管理场景

库存调整
@Service
public class InventoryService {
  
    @Transactional
    public void adjustInventory(InventoryAdjustmentRequest request) {
        // 1. 查询商品库存
        Product product = productRepository.findById(request.getProductId());
        if (product == null) {
            throw new ProductNotFoundException("商品不存在");
        }
      
        // 2. 验证调整权限
        if (!hasAdjustmentPermission(request.getOperatorId(), product.getCategoryId())) {
            throw new InsufficientPermissionException("无权限调整库存");
        }
      
        // 3. 更新库存
        int oldStock = product.getStock();
        int newStock = oldStock + request.getAdjustmentAmount();
      
        if (newStock < 0) {
            throw new InvalidStockException("库存不能为负数");
        }
      
        product.setStock(newStock);
        productRepository.save(product);
      
        // 4. 记录库存变动
        InventoryChange change = new InventoryChange();
        change.setProductId(request.getProductId());
        change.setOldStock(oldStock);
        change.setNewStock(newStock);
        change.setChangeAmount(request.getAdjustmentAmount());
        change.setChangeType(request.getChangeType());
        change.setOperatorId(request.getOperatorId());
        change.setChangeTime(LocalDateTime.now());
        change.setReason(request.getReason());
        inventoryChangeRepository.save(change);
      
        // 5. 发送库存预警
        if (newStock < product.getMinStock()) {
            notificationService.sendLowStockAlert(product);
        }
    }
}

5. 日志记录场景

操作日志
@Service
public class OperationLogService {
  
    @Transactional(propagation = Propagation.REQUIRES_NEW) // 独立事务
    public void recordOperationLog(OperationLogRequest request) {
        try {
            // 1. 创建操作日志
            OperationLog log = new OperationLog();
            log.setUserId(request.getUserId());
            log.setModule(request.getModule());
            log.setOperation(request.getOperation());
            log.setDescription(request.getDescription());
            log.setRequestData(request.getRequestData());
            log.setResponseData(request.getResponseData());
            log.setStatus(request.getStatus());
            log.setExecuteTime(request.getExecuteTime());
            log.setCreateTime(LocalDateTime.now());
          
            // 2. 保存日志
            operationLogRepository.save(log);
          
            // 3. 异步处理(不影响主事务)
            asyncProcessLog(log);
          
        } catch (Exception e) {
            // 日志记录失败不影响主业务
            log.error("操作日志记录失败", e);
        }
    }
}

6. 数据同步场景

主从数据同步
@Service
public class DataSyncService {
  
    @Transactional
    public void syncUserData(Long userId) {
        // 1. 查询主库用户数据
        User masterUser = masterUserRepository.findById(userId);
        if (masterUser == null) {
            throw new UserNotFoundException("用户不存在");
        }
      
        // 2. 查询从库用户数据
        User slaveUser = slaveUserRepository.findById(userId);
      
        // 3. 比较数据差异
        if (slaveUser == null || !isDataConsistent(masterUser, slaveUser)) {
            // 4. 同步到从库
            if (slaveUser == null) {
                slaveUser = new User();
                slaveUser.setId(masterUser.getId());
            }
          
            // 复制主库数据
            BeanUtils.copyProperties(masterUser, slaveUser);
            slaveUser.setSyncTime(LocalDateTime.now());
            slaveUserRepository.save(slaveUser);
          
            // 5. 记录同步日志
            DataSyncLog syncLog = new DataSyncLog();
            syncLog.setTableName("users");
            syncLog.setRecordId(userId);
            syncLog.setSyncType("UPDATE");
            syncLog.setSyncTime(LocalDateTime.now());
            dataSyncLogRepository.save(syncLog);
        }
    }
}

事务应用场景总结

场景类型特点事务要求示例
金融交易数据一致性要求高强事务银行转账、支付处理
电商订单业务完整性重要强事务订单创建、库存管理
用户管理数据关联性强中等事务用户注册、权限管理
库存管理实时性要求高强事务库存调整、预警通知
日志记录非关键业务弱事务操作日志、审计日志
数据同步一致性要求高强事务主从同步、数据迁移

事务在这些场景中发挥着关键作用,确保数据的一致性和业务的完整性。选择合适的传播特性和隔离级别,可以优化性能并满足不同的业务需求。


结合Spring使用案例

Spring事务管理概述

Spring提供了声明式和编程式两种事务管理方式,通过@Transactional注解和TransactionTemplate实现。

1. 声明式事务管理

基本配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
  
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
服务层事务配置
@Service
@Transactional
public class UserService {
  
    @Autowired
    private UserRepository userRepository;
  
    @Autowired
    private UserProfileRepository profileRepository;
  
    @Autowired
    private EmailService emailService;
  
    // 默认事务配置
    public User createUser(User user) {
        // 验证用户信息
        validateUser(user);
      
        // 保存用户
        User savedUser = userRepository.save(user);
      
        // 创建用户档案
        UserProfile profile = new UserProfile();
        profile.setUserId(savedUser.getId());
        profile.setNickname(user.getUsername());
        profileRepository.save(profile);
      
        // 发送欢迎邮件
        emailService.sendWelcomeEmail(savedUser.getEmail());
      
        return savedUser;
    }
  
    // 自定义事务配置
    @Transactional(
        propagation = Propagation.REQUIRES_NEW,
        isolation = Isolation.READ_COMMITTED,
        timeout = 30,
        rollbackFor = {UserException.class, RuntimeException.class}
    )
    public void updateUserProfile(Long userId, UserProfile profile) {
        UserProfile existingProfile = profileRepository.findByUserId(userId);
        if (existingProfile == null) {
            throw new UserNotFoundException("用户档案不存在");
        }
      
        // 更新档案信息
        existingProfile.setNickname(profile.getNickname());
        existingProfile.setAvatar(profile.getAvatar());
        existingProfile.setUpdateTime(LocalDateTime.now());
      
        profileRepository.save(existingProfile);
    }
  
    // 只读事务
    @Transactional(readOnly = true)
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用户不存在"));
    }
}
控制器层事务处理
@RestController
@RequestMapping("/api/users")
public class UserController {
  
    @Autowired
    private UserService userService;
  
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        try {
            User createdUser = userService.createUser(user);
            return ResponseEntity.ok(createdUser);
        } catch (UserException e) {
            // 业务异常,事务已回滚
            return ResponseEntity.badRequest().build();
        } catch (Exception e) {
            // 系统异常,事务已回滚
            return ResponseEntity.status(500).build();
        }
    }
  
    @PutMapping("/{userId}/profile")
    public ResponseEntity<String> updateProfile(
            @PathVariable Long userId,
            @RequestBody UserProfile profile) {
        try {
            userService.updateUserProfile(userId, profile);
            return ResponseEntity.ok("更新成功");
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        } catch (Exception e) {
            return ResponseEntity.status(500).body("更新失败");
        }
    }
}

2. 编程式事务管理

TransactionTemplate使用
@Service
public class OrderService {
  
    @Autowired
    private TransactionTemplate transactionTemplate;
  
    @Autowired
    private OrderRepository orderRepository;
  
    @Autowired
    private InventoryService inventoryService;
  
    public void createOrderWithTemplate(OrderRequest request) {
        // 使用TransactionTemplate执行事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 创建订单
                    Order order = new Order();
                    order.setUserId(request.getUserId());
                    order.setStatus(OrderStatus.CREATED);
                    order.setCreateTime(LocalDateTime.now());
                    orderRepository.save(order);
                  
                    // 扣减库存
                    inventoryService.reduceStock(request.getProductId(), request.getQuantity());
                  
                    // 如果执行成功,事务自动提交
                    // 如果抛出异常,事务自动回滚
                  
                } catch (Exception e) {
                    // 手动设置回滚
                    status.setRollbackOnly();
                    throw new RuntimeException("订单创建失败", e);
                }
            }
        });
    }
  
    // 使用Lambda表达式简化代码
    public void createOrderWithLambda(OrderRequest request) {
        transactionTemplate.execute(status -> {
            try {
                // 创建订单
                Order order = new Order();
                order.setUserId(request.getUserId());
                order.setStatus(OrderStatus.CREATED);
                order.setCreateTime(LocalDateTime.now());
                orderRepository.save(order);
              
                // 扣减库存
                inventoryService.reduceStock(request.getProductId(), request.getQuantity());
              
                return order;
              
            } catch (Exception e) {
                status.setRollbackOnly();
                throw new RuntimeException("订单创建失败", e);
            }
        });
    }
}
手动事务管理
@Service
public class ComplexOrderService {
  
    @Autowired
    private PlatformTransactionManager transactionManager;
  
    public void processComplexOrder(ComplexOrderRequest request) {
        // 创建事务定义
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        def.setTimeout(60);
      
        // 开始事务
        TransactionStatus status = transactionManager.getTransaction(def);
      
        try {
            // 第一步:验证订单
            validateOrder(request);
          
            // 第二步:创建订单
            Order order = createOrder(request);
          
            // 第三步:处理支付
            processPayment(order, request.getPaymentInfo());
          
            // 第四步:更新库存
            updateInventory(request.getItems());
          
            // 第五步:发送通知
            sendNotifications(order);
          
            // 提交事务
            transactionManager.commit(status);
          
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback(status);
            throw new RuntimeException("复杂订单处理失败", e);
        }
    }
}

3. 事务事件处理

事务事件监听器
@Component
public class TransactionEventListener {
  
    @EventListener
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUserCreatedAfterCommit(UserCreatedEvent event) {
        // 事务提交后执行
        log.info("用户创建成功,开始异步处理: {}", event.getUserId());
      
        // 发送欢迎邮件
        emailService.sendWelcomeEmailAsync(event.getUserEmail());
      
        // 更新用户统计
        userStatisticsService.incrementUserCount();
    }
  
    @EventListener
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleUserCreatedAfterRollback(UserCreatedEvent event) {
        // 事务回滚后执行
        log.warn("用户创建失败,清理相关资源: {}", event.getUserId());
      
        // 清理临时文件
        fileService.cleanupTempFiles(event.getUserId());
      
        // 记录失败日志
        auditService.recordFailure(event.getUserId(), "用户创建失败");
    }
}
自定义事务事件
@Component
public class OrderEventPublisher {
  
    @Autowired
    private ApplicationEventPublisher eventPublisher;
  
    @Transactional
    public void createOrder(OrderRequest request) {
        // 创建订单
        Order order = orderRepository.save(createOrderFromRequest(request));
      
        // 发布订单创建事件
        OrderCreatedEvent event = new OrderCreatedEvent(this, order);
        eventPublisher.publishEvent(event);
      
        // 事件在事务提交后处理
    }
}

public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
  
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
  
    public Order getOrder() {
        return order;
    }
}

4. 分布式事务处理

使用Seata实现分布式事务
@Service
public class DistributedOrderService {
  
    @GlobalTransactional // Seata全局事务注解
    public void createDistributedOrder(DistributedOrderRequest request) {
        // 1. 创建订单(本地事务)
        Order order = orderService.createOrder(request.getOrderInfo());
      
        // 2. 扣减库存(远程服务)
        inventoryService.reduceStock(request.getProductId(), request.getQuantity());
      
        // 3. 扣减用户余额(远程服务)
        accountService.debit(request.getUserId(), request.getAmount());
      
        // 4. 发送通知(远程服务)
        notificationService.sendOrderNotification(order);
      
        // 如果任何步骤失败,所有操作都会回滚
    }
}
使用消息队列实现最终一致性
@Service
public class EventualConsistencyOrderService {
  
    @Autowired
    private OrderService orderService;
  
    @Autowired
    private MessageSender messageSender;
  
    @Transactional
    public void createOrderWithEventualConsistency(OrderRequest request) {
        // 1. 创建订单(本地事务)
        Order order = orderService.createOrder(request);
      
        // 2. 发送消息到消息队列
        OrderCreatedMessage message = new OrderCreatedMessage();
        message.setOrderId(order.getId());
        message.setUserId(order.getUserId());
        message.setAmount(order.getTotalAmount());
      
        messageSender.sendMessage("order.created", message);
      
        // 消息发送失败不影响订单创建
        // 通过消息重试机制实现最终一致性
    }
}

@Component
public class OrderMessageConsumer {
  
    @RabbitListener(queues = "order.created")
    public void handleOrderCreated(OrderCreatedMessage message) {
        try {
            // 处理订单创建后的业务逻辑
            inventoryService.reduceStock(message.getProductId(), message.getQuantity());
            accountService.debit(message.getUserId(), message.getAmount());
          
        } catch (Exception e) {
            // 处理失败,消息会重新投递
            log.error("处理订单创建消息失败", e);
            throw e;
        }
    }
}

5. 事务性能优化

批量操作优化
@Service
public class BatchUserService {
  
    @Transactional
    public void batchCreateUsers(List<User> users) {
        // 分批处理,避免事务过大
        int batchSize = 100;
        for (int i = 0; i < users.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, users.size());
            List<User> batch = users.subList(i, endIndex);
          
            // 处理当前批次
            processUserBatch(batch);
        }
    }
  
    private void processUserBatch(List<User> users) {
        for (User user : users) {
            userRepository.save(user);
            userProfileRepository.save(createUserProfile(user));
        }
    }
}
读写分离优化
@Service
public class UserQueryService {
  
    @Transactional(readOnly = true)
    public List<User> searchUsers(UserSearchCriteria criteria) {
        // 只读事务,可以路由到读库
        return userRepository.searchUsers(criteria);
    }
  
    @Transactional(readOnly = true)
    public User getUserById(Long userId) {
        // 只读事务,使用读库
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用户不存在"));
    }
}

@Service
public class UserWriteService {
  
    @Transactional
    public User createUser(User user) {
        // 写事务,使用写库
        return userRepository.save(user);
    }
  
    @Transactional
    public void updateUser(User user) {
        // 写事务,使用写库
        userRepository.save(user);
    }
}

事务与数据库关系

数据库事务支持概述

不同数据库对事务的支持程度不同,理解这些差异有助于正确使用事务。

1. MySQL事务支持

存储引擎对比
-- 查看表使用的存储引擎
SHOW TABLE STATUS WHERE Name = 'users';

-- 查看数据库默认存储引擎
SHOW VARIABLES LIKE 'default_storage_engine';

-- 查看当前事务隔离级别
SELECT @@tx_isolation;

-- 查看自动提交设置
SELECT @@autocommit;
InnoDB事务特性
-- 创建支持事务的InnoDB表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(100) UNIQUE NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    status ENUM('ACTIVE', 'INACTIVE', 'DELETED') DEFAULT 'ACTIVE',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_username (username),
    INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 开始事务
START TRANSACTION;

-- 插入数据
INSERT INTO users (username, email) VALUES ('testuser', 'test@example.com');

-- 查询数据
SELECT * FROM users WHERE username = 'testuser';

-- 提交事务
COMMIT;

-- 或者回滚事务
ROLLBACK;
事务隔离级别设置
-- 设置会话级别事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 设置全局级别事务隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 查看所有事务隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';

2. PostgreSQL事务支持

事务特性
-- 开始事务
BEGIN;

-- 或者使用
START TRANSACTION;

-- 设置事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 设置事务为只读
SET TRANSACTION READ ONLY;

-- 设置事务为可延迟
SET TRANSACTION DEFERRABLE;

-- 执行SQL操作
INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com');
UPDATE users SET status = 'ACTIVE' WHERE username = 'user1';

-- 提交事务
COMMIT;

-- 或者回滚事务
ROLLBACK;
保存点使用
BEGIN;

INSERT INTO users (username, email) VALUES ('user1', 'user1@example.com');

-- 创建保存点
SAVEPOINT sp1;

INSERT INTO users (username, email) VALUES ('user2', 'user2@example.com');

-- 回滚到保存点
ROLLBACK TO SAVEPOINT sp1;

-- 继续操作
INSERT INTO users (username, email) VALUES ('user3', 'user3@example.com');

COMMIT;

3. Oracle事务支持

事务管理
-- 开始事务(Oracle默认自动提交关闭)
-- 不需要显式BEGIN

-- 执行SQL操作
INSERT INTO users (user_id, username, email) VALUES (1, 'user1', 'user1@example.com');
UPDATE users SET status = 'ACTIVE' WHERE user_id = 1;

-- 提交事务
COMMIT;

-- 或者回滚事务
ROLLBACK;

-- 创建保存点
SAVEPOINT before_update;

UPDATE users SET status = 'INACTIVE' WHERE user_id = 1;

-- 回滚到保存点
ROLLBACK TO SAVEPOINT before_update;
事务隔离级别
-- 设置事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 设置事务为只读
SET TRANSACTION READ ONLY;

-- 设置事务为串行化
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

4. 数据库事务差异对比

特性MySQL InnoDBPostgreSQLOracle
默认隔离级别REPEATABLE_READREAD_COMMITTEDREAD_COMMITTED
自动提交默认开启默认开启默认关闭
保存点支持支持支持
分布式事务有限支持支持支持
死锁检测支持支持支持
事务超时支持支持支持

5. 数据库连接池配置

HikariCP配置
# application.yml
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 60000
      # 事务相关配置
      auto-commit: false
      transaction-isolation: TRANSACTION_READ_COMMITTED
Druid配置
# application.yml
spring:
  datasource:
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      # 事务相关配置
      default-auto-commit: false
      default-transaction-isolation: 2 # READ_COMMITTED
      # 连接池监控
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: admin
        login-password: admin

6. 数据库事务监控

事务状态监控
@Component
public class TransactionMonitor {
  
    @EventListener
    public void handleTransactionEvent(TransactionEvent event) {
        if (event.getStatus() == TransactionStatus.COMMITTED) {
            log.info("事务提交成功: {}", event.getTransactionName());
            // 记录事务成功统计
            recordTransactionSuccess(event);
        } else if (event.getStatus() == TransactionStatus.ROLLED_BACK) {
            log.warn("事务回滚: {}", event.getTransactionName());
            // 记录事务失败统计
            recordTransactionFailure(event);
        }
    }
  
    private void recordTransactionSuccess(TransactionEvent event) {
        // 记录事务成功指标
        meterRegistry.counter("transaction.success", 
            "name", event.getTransactionName()).increment();
    }
  
    private void recordTransactionFailure(TransactionEvent event) {
        // 记录事务失败指标
        meterRegistry.counter("transaction.failure", 
            "name", event.getTransactionName()).increment();
    }
}
数据库连接监控
@Component
public class DatabaseConnectionMonitor {
  
    @Autowired
    private DataSource dataSource;
  
    @Scheduled(fixedRate = 30000) // 每30秒检查一次
    public void monitorDatabaseConnections() {
        if (dataSource instanceof HikariDataSource) {
            HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
            HikariPoolMXBean poolMXBean = hikariDataSource.getHikariPoolMXBean();
          
            log.info("数据库连接池状态: 活跃连接={}, 空闲连接={}, 总连接={}", 
                poolMXBean.getActiveConnections(),
                poolMXBean.getIdleConnections(),
                poolMXBean.getTotalConnections());
        }
    }
}

重难点分析

事务重难点概述

事务管理中的重难点包括并发控制、死锁处理、性能优化、分布式事务等。

1. 并发控制问题

问题描述

多个事务并发执行时,可能出现数据不一致、丢失更新、幻读等问题。

解决方案
@Service
public class ConcurrentOrderService {
  
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void processConcurrentOrder(OrderRequest request) {
        // 使用最高隔离级别,避免并发问题
        // 但性能较低,适合关键业务
      
        // 1. 悲观锁:SELECT FOR UPDATE
        Product product = productRepository.findByIdWithLock(request.getProductId());
      
        // 2. 检查库存
        if (product.getStock() < request.getQuantity()) {
            throw new InsufficientStockException("库存不足");
        }
      
        // 3. 更新库存
        product.setStock(product.getStock() - request.getQuantity());
        productRepository.save(product);
      
        // 4. 创建订单
        Order order = createOrder(request);
        orderRepository.save(order);
    }
}

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
  
    @Query("SELECT p FROM Product p WHERE p.id = :id")
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Product findByIdWithLock(@Param("id") Long id);
}
乐观锁实现
@Entity
public class Product {
  
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
  
    private String name;
    private Integer stock;
  
    @Version
    private Integer version; // 乐观锁版本号
  
    // getters and setters
}

@Service
public class OptimisticLockService {
  
    @Transactional
    public void updateProductWithOptimisticLock(Long productId, Integer newStock) {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new ProductNotFoundException("商品不存在"));
      
        // 更新库存
        product.setStock(newStock);
      
        try {
            productRepository.save(product);
        } catch (ObjectOptimisticLockingFailureException e) {
            // 版本冲突,重新获取数据
            log.warn("乐观锁冲突,重新处理: {}", productId);
            retryUpdateProduct(productId, newStock);
        }
    }
  
    private void retryUpdateProduct(Long productId, Integer newStock) {
        // 重试逻辑
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new ProductNotFoundException("商品不存在"));
      
        product.setStock(newStock);
        productRepository.save(product);
    }
}

2. 死锁处理

死锁检测
@Service
public class DeadlockDetectionService {
  
    @EventListener
    public void handleDeadlockEvent(DeadlockEvent event) {
        log.error("检测到死锁: {}", event.getDetails());
      
        // 记录死锁信息
        recordDeadlock(event);
      
        // 发送告警
        alertService.sendDeadlockAlert(event);
      
        // 尝试自动恢复
        tryAutoRecovery(event);
    }
  
    private void tryAutoRecovery(DeadlockEvent event) {
        // 根据死锁类型选择恢复策略
        if (event.getType() == DeadlockType.ORDER_INVENTORY) {
            // 订单库存死锁,优先处理订单
            prioritizeOrderProcessing();
        } else if (event.getType() == DeadlockType.USER_PROFILE) {
            // 用户档案死锁,优先处理用户
            prioritizeUserProcessing();
        }
    }
}
死锁预防
@Service
public class DeadlockPreventionService {
  
    @Transactional
    public void processOrderWithDeadlockPrevention(OrderRequest request) {
        // 1. 按固定顺序访问资源,避免死锁
        List<Long> productIds = request.getItems().stream()
            .map(OrderItem::getProductId)
            .sorted() // 排序,确保访问顺序一致
            .collect(Collectors.toList());
      
        // 2. 批量锁定资源
        List<Product> products = productRepository.findAllByIdWithLock(productIds);
      
        // 3. 验证库存
        for (Product product : products) {
            if (product.getStock() < getRequestedQuantity(request, product.getId())) {
                throw new InsufficientStockException("库存不足: " + product.getName());
            }
        }
      
        // 4. 更新库存
        for (Product product : products) {
            int requestedQuantity = getRequestedQuantity(request, product.getId());
            product.setStock(product.getStock() - requestedQuantity);
            productRepository.save(product);
        }
      
        // 5. 创建订单
        Order order = createOrder(request);
        orderRepository.save(order);
    }
}

3. 长事务处理

问题描述

长事务会占用数据库连接,影响并发性能,增加死锁风险。

解决方案
@Service
public class LongTransactionService {
  
    // 避免长事务,将大操作拆分为小操作
    @Transactional
    public void processLargeDataWithoutLongTransaction(List<DataItem> items) {
        // 分批处理,每批1000条
        int batchSize = 1000;
        for (int i = 0; i < items.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, items.size());
            List<DataItem> batch = items.subList(i, endIndex);
          
            // 每批单独处理
            processBatch(batch);
        }
    }
  
    @Transactional
    public void processBatch(List<DataItem> batch) {
        for (DataItem item : batch) {
            // 处理单个数据项
            processItem(item);
        }
    }
  
    // 使用异步处理避免长事务
    @Async
    public CompletableFuture<Void> processDataAsync(List<DataItem> items) {
        return CompletableFuture.runAsync(() -> {
            processLargeDataWithoutLongTransaction(items);
        });
    }
}
事务超时设置
@Service
public class TimeoutTransactionService {
  
    @Transactional(timeout = 30) // 30秒超时
    public void processWithTimeout(List<DataItem> items) {
        // 如果处理时间超过30秒,事务自动回滚
      
        for (DataItem item : items) {
            // 检查是否接近超时
            if (isNearTimeout()) {
                throw new TransactionTimeoutException("接近事务超时,请分批处理");
            }
          
            processItem(item);
        }
    }
  
    private boolean isNearTimeout() {
        // 检查当前事务是否接近超时
        // 实现超时检查逻辑
        return false;
    }
}

4. 分布式事务一致性

问题描述

在微服务架构中,跨服务的事务难以保证一致性。

解决方案
@Service
public class DistributedTransactionService {
  
    // 使用Saga模式实现分布式事务
    public void processOrderWithSaga(OrderRequest request) {
        try {
            // 1. 创建订单
            Order order = orderService.createOrder(request);
          
            // 2. 扣减库存
            inventoryService.reduceStock(request.getProductId(), request.getQuantity());
          
            // 3. 扣减余额
            accountService.debit(request.getUserId(), request.getAmount());
          
            // 4. 发送通知
            notificationService.sendOrderNotification(order);
          
        } catch (Exception e) {
            // 补偿操作
            compensateOrder(request);
        }
    }
  
    private void compensateOrder(OrderRequest request) {
        try {
            // 1. 恢复库存
            inventoryService.restoreStock(request.getProductId(), request.getQuantity());
          
            // 2. 恢复余额
            accountService.credit(request.getUserId(), request.getAmount());
          
            // 3. 取消订单
            orderService.cancelOrder(request.getOrderId());
          
        } catch (Exception compensationException) {
            // 补偿失败,记录日志,人工处理
            log.error("补偿操作失败", compensationException);
            manualCompensationService.recordCompensationFailure(request);
        }
    }
}

5. 事务性能优化

批量操作优化
@Service
public class BatchOptimizationService {
  
    @Transactional
    public void batchInsertUsers(List<User> users) {
        // 使用JPA批量插入
        int batchSize = 100;
        for (int i = 0; i < users.size(); i += batchSize) {
            List<User> batch = users.subList(i, Math.min(i + batchSize, users.size()));
            userRepository.saveAll(batch);
          
            // 刷新实体管理器,避免内存溢出
            entityManager.flush();
            entityManager.clear();
        }
    }
  
    // 使用原生SQL批量插入
    @Transactional
    public void batchInsertUsersWithNativeSQL(List<User> users) {
        String sql = "INSERT INTO users (username, email, status) VALUES (?, ?, ?)";
      
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                User user = users.get(i);
                ps.setString(1, user.getUsername());
                ps.setString(2, user.getEmail());
                ps.setString(3, user.getStatus().name());
            }
          
            @Override
            public int getBatchSize() {
                return users.size();
            }
        });
    }
}
读写分离优化
@Service
public class ReadWriteSeparationService {
  
    @Transactional(readOnly = true)
    public List<User> searchUsers(UserSearchCriteria criteria) {
        // 只读操作,使用读库
        return userRepository.searchUsers(criteria);
    }
  
    @Transactional
    public User createUser(User user) {
        // 写操作,使用写库
        return userRepository.save(user);
    }
  
    // 强制使用读库
    @Transactional(readOnly = true)
    @ReadOnly
    public User getUserById(Long userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("用户不存在"));
    }
}

高频面试点

事务面试高频问题概述

事务是面试中的重点内容,掌握这些高频问题有助于面试成功。

1. 基础概念类问题

Q1: 什么是事务?事务的ACID特性是什么?

A: 事务是数据库管理系统中的一个逻辑操作单元,包含一组操作,要么全部成功,要么全部失败。

ACID特性:

  • 原子性(Atomicity): 事务中的所有操作要么全部成功,要么全部失败
  • 一致性(Consistency): 事务执行前后,数据库从一个一致状态转换到另一个一致状态
  • 隔离性(Isolation): 多个事务并发执行时,事务之间相互隔离
  • 持久性(Durability): 一旦事务提交,其所做的修改就会永久保存在数据库中
Q2: 事务隔离级别有哪些?各有什么特点?

A: 事务隔离级别从低到高依次为:

隔离级别脏读不可重复读幻读性能并发性
读未提交最高最高
读已提交
可重复读
串行化最低最低

特点:

  • 读未提交: 性能最高,但存在所有并发问题
  • 读已提交: 解决了脏读,性能较高,适合大多数应用
  • 可重复读: 解决了脏读和不可重复读,MySQL默认级别
  • 串行化: 解决了所有并发问题,但性能最低
Q3: 什么是脏读、不可重复读、幻读?

A:

  • 脏读: 一个事务读取了另一个未提交事务的数据
  • 不可重复读: 一个事务内多次读取同一数据,得到不同结果
  • 幻读: 一个事务内多次查询,结果集的行数不同

2. Spring事务管理类问题

Q4: Spring中如何实现事务管理?

A: Spring提供两种事务管理方式:

声明式事务:

@Transactional
public void createUser(User user) {
    userRepository.save(user);
}

编程式事务:

@Autowired
private TransactionTemplate transactionTemplate;

public void createUser(User user) {
    transactionTemplate.execute(status -> {
        userRepository.save(user);
        return user;
    });
}
Q5: @Transactional注解有哪些属性?

A: 主要属性包括:

@Transactional(
    propagation = Propagation.REQUIRED,        // 传播特性
    isolation = Isolation.READ_COMMITTED,      // 隔离级别
    timeout = 30,                             // 超时时间
    readOnly = false,                         // 是否只读
    rollbackFor = Exception.class,            // 回滚异常
    noRollbackFor = RuntimeException.class    // 不回滚异常
)
Q6: 事务传播特性有哪些?各有什么作用?

A: 主要传播特性:

  • REQUIRED: 默认,如果当前存在事务则加入,否则创建新事务
  • REQUIRES_NEW: 总是创建新事务,如果当前存在事务则挂起
  • SUPPORTS: 如果当前存在事务则加入,否则以非事务方式执行
  • NOT_SUPPORTED: 以非事务方式执行,如果当前存在事务则挂起
  • MANDATORY: 必须在事务中执行,否则抛出异常
  • NEVER: 不能在事务中执行,否则抛出异常
  • NESTED: 如果当前存在事务则创建嵌套事务,否则等同于REQUIRED

3. 事务失效类问题

Q7: 什么情况下@Transactional注解会失效?

A: 主要失效情况:

  1. 方法访问权限问题: 私有方法上的@Transactional无效
  2. 方法自调用问题: 同一类中直接调用,事务注解无效
  3. 异常类型问题: 受检异常默认不回滚
  4. 异常被捕获: 异常被捕获后不会触发回滚
  5. 数据库引擎不支持: MyISAM引擎不支持事务
  6. 事务方法被非事务方法调用: 外层方法需要事务注解
Q8: 如何解决事务失效问题?

A: 解决方案:

// 1. 确保方法是public
@Transactional
public void createUser(User user) {
    // 方法实现
}

// 2. 解决自调用问题
@Autowired
private UserService self;

@Transactional
public void createUser(User user) {
    self.updateUserCache(user); // 通过代理调用
}

// 3. 配置回滚异常
@Transactional(rollbackFor = Exception.class)
public void createUser(User user) throws Exception {
    // 方法实现
}

4. 性能优化类问题

Q9: 如何优化事务性能?

A: 主要优化策略:

  1. 合理设置隔离级别: 根据业务需求选择最低的隔离级别
  2. 避免长事务: 将大操作拆分为小操作
  3. 使用批量操作: 减少数据库交互次数
  4. 读写分离: 读操作使用只读事务
  5. 合理设置超时: 避免事务长时间占用资源
Q10: 什么是死锁?如何预防死锁?

A:
死锁: 两个或多个事务互相等待对方释放锁,导致无法继续执行。

预防策略:

  1. 固定顺序访问资源: 按固定顺序访问表或行
  2. 减少事务持有锁的时间: 尽快释放锁
  3. 使用乐观锁: 避免悲观锁带来的死锁风险
  4. 设置锁超时: 避免无限等待

5. 分布式事务类问题

Q11: 什么是分布式事务?有哪些解决方案?

A:
分布式事务: 涉及多个数据库或服务的事务操作。

解决方案:

  1. 2PC(两阶段提交): 准备阶段和提交阶段
  2. 3PC(三阶段提交): 增加预提交阶段
  3. TCC(Try-Confirm-Cancel): 尝试-确认-取消模式
  4. Saga模式: 长事务拆分为短事务
  5. 最终一致性: 通过消息队列实现最终一致性
Q12: 如何实现最终一致性?

A: 实现方式:

@Service
public class EventualConsistencyService {
  
    @Transactional
    public void createOrder(OrderRequest request) {
        // 1. 创建订单(本地事务)
        Order order = orderService.createOrder(request);
      
        // 2. 发送消息到消息队列
        OrderCreatedEvent event = new OrderCreatedEvent(order);
        messageSender.sendMessage("order.created", event);
      
        // 消息发送失败不影响订单创建
        // 通过消息重试机制实现最终一致性
    }
}

@Component
public class OrderEventConsumer {
  
    @RabbitListener(queues = "order.created")
    public void handleOrderCreated(OrderCreatedEvent event) {
        try {
            // 处理订单创建后的业务逻辑
            inventoryService.reduceStock(event.getProductId(), event.getQuantity());
            accountService.debit(event.getUserId(), event.getAmount());
          
        } catch (Exception e) {
            // 处理失败,消息会重新投递
            throw e;
        }
    }
}

6. 实际应用类问题

Q13: 在电商系统中,如何保证订单和库存的一致性?

A: 解决方案:

@Service
public class OrderInventoryService {
  
    @Transactional
    public void createOrder(OrderRequest request) {
        // 1. 验证库存
        for (OrderItem item : request.getItems()) {
            Product product = productRepository.findById(item.getProductId());
            if (product.getStock() < item.getQuantity()) {
                throw new InsufficientStockException("库存不足");
            }
        }
      
        // 2. 创建订单
        Order order = orderRepository.save(createOrderFromRequest(request));
      
        // 3. 扣减库存
        for (OrderItem item : request.getItems()) {
            Product product = productRepository.findById(item.getProductId());
            product.setStock(product.getStock() - item.getQuantity());
            productRepository.save(product);
        }
      
        // 如果任何步骤失败,整个事务回滚
    }
}
Q14: 如何处理高并发下的库存超卖问题?

A: 解决方案:

@Service
public class HighConcurrencyInventoryService {
  
    @Transactional
    public void reduceStockWithLock(Long productId, Integer quantity) {
        // 1. 使用悲观锁锁定商品
        Product product = productRepository.findByIdWithLock(productId);
      
        // 2. 检查库存
        if (product.getStock() < quantity) {
            throw new InsufficientStockException("库存不足");
        }
      
        // 3. 扣减库存
        product.setStock(product.getStock() - quantity);
        productRepository.save(product);
    }
  
    // 或者使用乐观锁
    @Transactional
    public void reduceStockWithOptimisticLock(Long productId, Integer quantity) {
        Product product = productRepository.findById(productId)
            .orElseThrow(() -> new ProductNotFoundException("商品不存在"));
      
        // 使用版本号控制并发
        if (product.getStock() < quantity) {
            throw new InsufficientStockException("库存不足");
        }
      
        product.setStock(product.getStock() - quantity);
        productRepository.save(product);
    }
}

面试技巧总结

  1. 理解概念: 深入理解ACID特性和隔离级别
  2. 掌握原理: 了解事务的实现原理和机制
  3. 实践应用: 能够结合实际场景分析问题
  4. 性能优化: 了解事务性能优化的方法
  5. 问题解决: 能够分析并解决事务相关问题

通过掌握这些高频面试点,可以在面试中展现出对事务管理的深入理解和实践经验。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值