欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > 透视 @Transactional 的隔离级别:四大隔离机制让事务更安全!

透视 @Transactional 的隔离级别:四大隔离机制让事务更安全!

2024/10/27 3:44:18 来源:https://blog.csdn.net/qq_35971258/article/details/143260554  浏览:    关键词:透视 @Transactional 的隔离级别:四大隔离机制让事务更安全!

文章目录

      • **一、READ_UNCOMMITTED(读未提交)**
        • **代码示例**
      • **二、READ_COMMITTED(读已提交)**
        • **代码示例**
      • **三、REPEATABLE_READ(可重复读)**
        • **代码示例**
      • **四、SERIALIZABLE(可串行化)**
        • **代码示例**
      • **总结**
      • 推荐阅读文章

正文

在日常开发中,使用 Spring 的 @Transactional 注解来管理事务几乎成了必备操作。虽然 @Transactional 的默认隔离级别大多情况下已经够用,但对于需要处理并发访问的场景,理解并正确设置隔离级别至关重要。隔离机制是我们应对数据库并发问题的“防护盾”,可以帮助我们避免“脏读”、“不可重复读”和“幻读”等常见并发问题。

今天就带大家一一深入了解 Isolation 中的四种隔离级别,通过实例搞清每一种的作用、适用场景及潜在风险。


一、READ_UNCOMMITTED(读未提交)

READ_UNCOMMITTED 允许事务读取其他事务未提交的数据,这种情况下,脏读是可能发生的,隔离级别最低,但并发性能最佳。

代码示例
@Service
public class ProductService {@Transactional(isolation = Isolation.READ_UNCOMMITTED)public List<Product> getProducts() {// 读取产品列表(可能会读取到其他事务未提交的数据)return productRepository.findAll();}
}

效果分析
在此隔离级别下,如果另一个事务正在修改产品的价格,但尚未提交,我们就有可能读取到它尚未提交的价格数据,出现“脏读”现象。虽然性能较高,但容易导致数据不一致,因此不适合在对数据准确性要求高的场景中使用。

适用场景
适合在对数据一致性要求不高,但需要高并发的场景。比如,大规模实时查询排名、缓存更新等操作。


二、READ_COMMITTED(读已提交)

READ_COMMITTED 是较常见的隔离级别,它确保事务只能读取到其他事务已提交的数据,从而避免了脏读,但依然会有不可重复读的情况。

代码示例
@Service
public class ProductService {@Transactional(isolation = Isolation.READ_COMMITTED)public Product getProductById(Long productId) {// 读取产品信息return productRepository.findById(productId).orElse(null);}
}

效果分析
在此隔离级别下,事务只能读取到已经提交的数据,避免了读取到脏数据。但如果在事务内多次读取同一个数据,而这个数据在事务期间被其他事务修改,我们会得到不同的结果,发生“不可重复读”。

适用场景
通常用于大多数企业级应用,对数据准确性有一定要求的同时,仍然需要较高的并发性能。适合常见的订单系统、库存管理等场景。


三、REPEATABLE_READ(可重复读)

REPEATABLE_READ 能保证在同一事务中多次读取的结果一致,防止“不可重复读”现象的发生。这也是 MySQL 默认的隔离级别。

代码示例
@Service
public class ProductService {@Transactional(isolation = Isolation.REPEATABLE_READ)public void checkProductAvailability(Long productId) {Product product = productRepository.findById(productId).orElse(null);// 假设检查产品库存是否充足if (product != null && product.getStock() > 0) {System.out.println("产品可用");}// 第二次检查product = productRepository.findById(productId).orElse(null);if (product != null && product.getStock() > 0) {System.out.println("再次确认,产品仍可用");}}
}

效果分析
在该隔离级别下,即使有其他事务在并发地修改产品库存,本事务在多次读取时也会获得一致的数据。因此,避免了不可重复读的现象。但在某些情况下,仍可能出现幻读,即当另一事务插入了新的行,这个事务的结果集却发生了变化。

适用场景
适用于电商、金融等需要保证数据一致性而对并发性能要求较高的场景,特别是在执行复杂的查询操作时。


四、SERIALIZABLE(可串行化)

SERIALIZABLE 是最高级别的隔离机制,它会强制事务串行执行,从而防止“脏读”、“不可重复读”及“幻读”的发生。但由于并发性能最差,通常不推荐在高并发场景使用。

代码示例
@Service
public class ProductService {@Transactional(isolation = Isolation.SERIALIZABLE)public void processOrder(Long productId) {Product product = productRepository.findById(productId).orElse(null);// 检查库存并处理订单if (product != null && product.getStock() > 0) {product.setStock(product.getStock() - 1);productRepository.save(product);System.out.println("订单处理成功");} else {System.out.println("库存不足");}}
}

效果分析
SERIALIZABLE 隔离级别下,所有读取和写入操作都被锁定,以确保事务之间不会产生任何并发问题。这种方式虽然可以完全避免并发带来的数据不一致问题,但并发性能会大幅下降,因为每次事务必须等待其他事务完成。

适用场景
一般用于要求极高数据一致性且并发量低的系统,比如金融系统的核心账务部分,确保不会发生数据的误操作和不一致。


总结

  • READ_UNCOMMITTED:适合低一致性需求、高并发场景,但容易出现脏读。
  • READ_COMMITTED:避免了脏读,适用于多数企业级应用,但仍会有不可重复读的情况。
  • REPEATABLE_READ:防止不可重复读,适合大多数需要数据一致性的应用,但可能会有幻读现象。
  • SERIALIZABLE:完全保证事务隔离,但并发性能最差,适用于一致性要求最高的场景。

在实际项目中,我们通常选择 READ_COMMITTEDREPEATABLE_READ,这两种隔离级别在数据一致性与并发性能之间取得了平衡。希望通过这些代码示例,大家能对隔离机制有更直观的理解,避免并发中的数据不一致问题!

推荐阅读文章

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
  • HTTP、HTTPS、Cookie 和 Session 之间的关系
  • 使用 Spring 框架构建 MVC 应用程序:初学者教程
  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
  • Java Spring 中常用的 @PostConstruct 注解使用总结
  • 线程 vs 虚拟线程:深入理解及区别
  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
  • 为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com