事务全面指南
目录
事务简介
什么是事务
事务(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);
// 事务提交后,数据永久保存
// 即使系统重启,数据也不会丢失
}
事务的生命周期
基础事务生命周期流程图
详细事务状态转换图
Spring事务生命周期简化流程图
Spring事务生命周期详细说明
| 阶段 | 描述 | 关键操作 | 技术细节 |
|---|---|---|---|
| 方法调用 | 客户端调用业务方法 | 方法参数验证、权限检查 | 通过Spring AOP拦截 |
| 注解检查 | 检查方法是否有事务注解 | 解析@Transactional属性 | 使用反射获取注解信息 |
| 代理创建 | 创建事务代理对象 | 生成代理类、设置拦截器 | 基于CGLIB或JDK动态代理 |
| 事务管理器 | 获取事务管理器实例 | 根据数据源选择管理器 | DataSourceTransactionManager等 |
| 事务定义 | 创建事务定义对象 | 设置传播特性、隔离级别 | DefaultTransactionDefinition |
| 开始事务 | 开始数据库事务 | 获取数据库连接、设置自动提交 | 调用Connection.setAutoCommit(false) |
| 属性设置 | 设置事务属性 | 隔离级别、超时时间、只读标志 | 应用事务定义中的配置 |
| 执行业务 | 执行实际的业务逻辑 | SQL执行、数据修改 | 在事务上下文中执行 |
| 结果判断 | 判断业务执行结果 | 异常检测、成功确认 | 捕获RuntimeException和Error |
| 事务提交 | 提交数据库事务 | 刷新日志、释放锁 | 调用Connection.commit() |
| 事务回滚 | 回滚数据库事务 | 撤销修改、释放锁 | 调用Connection.rollback() |
| 资源释放 | 释放事务相关资源 | 关闭连接、清理缓存 | 确保资源不泄漏 |
| 返回结果 | 返回方法执行结果 | 异常传播、结果封装 | 保持方法签名一致 |
数据库事务内部流程
事务并发控制流程图
事务超时处理流程图
分布式事务生命周期
事务生命周期各阶段说明
| 阶段 | 描述 | 关键操作 | 注意事项 |
|---|---|---|---|
| 开始阶段 | 事务初始化 | 分配事务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 InnoDB | PostgreSQL | Oracle |
|---|---|---|---|
| 默认隔离级别 | REPEATABLE_READ | READ_COMMITTED | READ_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: 主要失效情况:
- 方法访问权限问题: 私有方法上的@Transactional无效
- 方法自调用问题: 同一类中直接调用,事务注解无效
- 异常类型问题: 受检异常默认不回滚
- 异常被捕获: 异常被捕获后不会触发回滚
- 数据库引擎不支持: MyISAM引擎不支持事务
- 事务方法被非事务方法调用: 外层方法需要事务注解
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: 主要优化策略:
- 合理设置隔离级别: 根据业务需求选择最低的隔离级别
- 避免长事务: 将大操作拆分为小操作
- 使用批量操作: 减少数据库交互次数
- 读写分离: 读操作使用只读事务
- 合理设置超时: 避免事务长时间占用资源
Q10: 什么是死锁?如何预防死锁?
A:
死锁: 两个或多个事务互相等待对方释放锁,导致无法继续执行。
预防策略:
- 固定顺序访问资源: 按固定顺序访问表或行
- 减少事务持有锁的时间: 尽快释放锁
- 使用乐观锁: 避免悲观锁带来的死锁风险
- 设置锁超时: 避免无限等待
5. 分布式事务类问题
Q11: 什么是分布式事务?有哪些解决方案?
A:
分布式事务: 涉及多个数据库或服务的事务操作。
解决方案:
- 2PC(两阶段提交): 准备阶段和提交阶段
- 3PC(三阶段提交): 增加预提交阶段
- TCC(Try-Confirm-Cancel): 尝试-确认-取消模式
- Saga模式: 长事务拆分为短事务
- 最终一致性: 通过消息队列实现最终一致性
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);
}
}
面试技巧总结
- 理解概念: 深入理解ACID特性和隔离级别
- 掌握原理: 了解事务的实现原理和机制
- 实践应用: 能够结合实际场景分析问题
- 性能优化: 了解事务性能优化的方法
- 问题解决: 能够分析并解决事务相关问题
通过掌握这些高频面试点,可以在面试中展现出对事务管理的深入理解和实践经验。
1312

被折叠的 条评论
为什么被折叠?



