写点什么

Spring 事务优化解析:实现高效率、高并发、低失效的全新策略

作者:xfgg
  • 2023-06-20
    福建
  • 本文字数:3953 字

    阅读完需:约 13 分钟

Spring事务优化解析:实现高效率、高并发、低失效的全新策略

引言

在当今高并发,高性能的应用场景下,数据不仅需要保证一致性,同时对于最小化系统开销,提高执行效率,保证高级别并发能力以及降低出错率也是必不可少的。本文将系统阐述 Spring 事务优化的方法及运用技巧,帮助读者掌握如何实现高效率、高并发、低失效的全新策略,并提高应用程序的整体执行效能。

Spring 事务管理基本原理

ACID 特性

  • 原子性(Atomicity)- 事务要么全部执行成功,要么全部失败回滚。

  • 一致性(Consistency)- 事务执行前后,数据库的状态始终保持一致。

  • 隔离性(Isolation)- 并发事务间相互隔离,不受其他事务影响。

  • 持久性(Durability)- 事务执行成功后,对数据的修改是永久的

管理机制

Spring 的事务管理机制是 Spring 框架中提供的一种扩展 API,Spring 的事务管理分为编程式事务和声明式事务两种方式。具体来讲,Spring 的事务管理机制提供如下特性:

  • 通过配置声明性事务,使得应用代码只专注于业务操作,而非手动进行事务管理;

  • 提供编程模型控制事务,开发者可以在代码中随时开启、提交或回滚事务;

  • 支持多种事务隔离级别,以及许多具有可插拔功能的完成器、传播行为等;

  • 支持多个事务实现:例如 Java Transaction API (JTA)、Hibernate Transaction API、Java Persistence API (JPA) 及 Java Data Objects (JDO) 等等,这样就能方便地融合第三方框架。

Spring 事务优化策略

事务传播行为优化

@Transactional(propagation = Propagation.REQUIRED)public void transfer(Double amount, String sourceAccount, String targetAccount) {    // Perform transfer logic here}
复制代码

事务传播是指一个方法在执行的时候,被调用的方法(含自身递归调用)中如果发生了事务,当前的方法应该如何被处理的规则。

Spring 将传播级别定义为 TransactionDefinition 接口中的几个常量:

  • REQUIRED : 如果当前存在事务,则加入该事务;否则,创建一个新的事务。这是最常用的默认传播级别。

  • SUPPORTS : 如果当前存在事务,则加入该事务;否则,不使用事务。

  • MANDATORY : 如果当前存在事务,则加入该事务;否则,抛出一个 IllegalTransactionStateException 异常。

  • REQUIRES_NEW : 创建一个新的事务,如果当前存在事务,则挂起当前事务。

  • NOT_SUPPORTED : 不使用事务,如果当前存在事务,则挂起当前事务。

  • NEVER : 不使用事务,如果当前存在事务,则抛出一个 IllegalTransactionStateException 异常。

  • NESTED : 使用一个单独的事务,在外围事务中可以设置一个回滚点,内部事务可以回滚到这个回滚点。如果当前存在事务,则嵌套在该事务中执行,如果当前不存在事务,则与 REQUIRED 的传播行为一样。


对于事务传播级别的优化,我们应该尽可能使用合适的传播级别,避免锁粒度过大或太小导致脏读、幻读等问题。比如:

  • 一些简单的查询可使用 NOT_SUPPORTED 传播级别,不产生事务,不占用数据库锁资源。

  • 更新操作尽量用 REQUIRED REQUIRES_NEW 传播级别避免发生脏读。

  • 如果外部事务占用了 Table-level 锁,内层事务建议使用 NESTED 传播级别。


同时,我们还可以针对不同场景进行系统级优化:

  • 拆分热点:TABLE 锁需要锁住整张表才能插入、更新和删除。尽量使用合适的分片方式将表进行拆分,各自对独立的片面进行插入、更新或删除,这样尽量缩小锁粒度。

  • 间歇性事务:对于间歇性事务,每一次提交时表面看起来比较均匀,但实际上并不连贯,集中提交时对锁资源的浪费较大。可以将间事务确立周期和数量,按照行的方式封装事务以达到减少锁的数量浪费系数。

正确认识和高效使用事务传播级别对保障系统数据一致性和并发能力有重要作用。

事务隔离级别优化

@Transactional(isolation = Isolation.READ_COMMITTED)public void transfer(Double amount, String sourceAccount, String targetAccount) {    // Perform transfer logic here}
复制代码

Spring 框架提供了五种标准的事务隔离级别,包括:

  • DEFAULT(默认值):使用后端数据库默认的隔离级别;

  • READ_UNCOMMITTED(读未提交):让事务可以读取到未被其他事务提交的数据,这会导致脏读、不可重复读和幻读等问题,不建议使用;

  • READ_COMMITTED(读已提交):保证一个事务读取到的数据是其他已经提交的事务的最新版本,这个级别会避免脏读问题,但是会遇到不可重复读和幻读;

  • REPEATABLE_READ(可重复读):保证一个事务多次读取同一记录的结果是一致的,比如在同一个事务里执行两次相同的查询,得到的结果必然相同。这个级别会避免脏读和不可重复读,但会遇到幻读问题;

  • SERIALIZABLE(串行化):最高的事务隔离级别,强制事务只能完全串行执行,这样避免了所有并发问题,但是会导致数据库性能大幅下降。

并发问题及其解决方案

  • 脏读:读取还未提交的事务数据。解决方法:设置事务隔离级别为 Read Committed(读已提交)。

  • 不可重复读:在同一事务中多次读取同一条记录,结果不一致。解决方法:设置事务隔离级别为 Repeatable Read(可重复读)。

  • 幻读:在同一事务中,数据被其他事务新增或删除。解决方法:设置事务隔离级别为 Serializable(串行化)。

Spring 事务隔离级别是为了控制多个并发事务之间是否能够看到彼此所做的修改,确保数据的正确性和一致性。在进行 Spring 事务隔离级别优化时,可以从以下几个方面入手。

  • 仔细选择合适的隔离级别:Spring 对于事务隔离级别提供了基本上所有标准的隔离级别。应该选择最符合实际业务的隔离级别。可以从默认的(READ COMMITTED)开始,然后评估更高(REPEATABLE READ/SERIALIZABLE)或更低(READ UNCOMMITTED)隔离级别是否显著提高了性能,但这取决于具体业务场景的可行性。

  • 给表和索引加锁:加锁的表和索引的开销非常低,可以显着提高并发修改的效率。

  • 减少事务嵌套:那些非必要的嵌套来源会损害性能,因为子事务将占用和父事务相同深度的锁和其相应的开销。

  • 优化业务流程:在事务前锁定更少的资源可以提高整体性能,而业务处理期可以长则长,不需要必要地在事务环境中完成,可以只锁定业务所需的一部分资源,在使用期间仅锁定这种资源。

  • 分库分表:适当分散责任区域会使所涉及软件和整个业务更少受到锁资源卡顿的影响,并且涉及软件的开销更小,还可以更好地适应其目标市场。

只读事务和非只读事务优化

@Transactional(readOnly = true)public User getUser(long userId) {    return userRepository.findById(userId).orElse(null);}
复制代码

只读事务优化:

  • 只读事务本质上不允许进行修改操作,这可以避免脏读、不可重复读等问题。因此,在进行查询操作时,可以尽量采用只读事务,从而提高事务并发性能。

  • 只读事务可以降低数据库的锁定资源,减少锁定冲突。避免长时间运行的查询操作引起的锁定问题。

  • 只读事务可以使用数据库的备份库作为实际查询的数据源,进一步减轻主库的压力。

非只读事务优化:

  • 非只读事务中,尽量采用乐观锁而不是悲观锁,降低锁定资源和冲突的几率。

  • 非只读事务适用于需要修改数据状态的场景。合理设置事务传播行为和隔离级别,以实现最佳性能。

  • 尽量减小事务的执行时间和执行范围,避免长时间或大范围的锁定。事务超时设置

事务超时设置

@Transactional(timeout = 30) // Timeout in secondspublic void transfer(Double amount, String sourceAccount, String targetAccount) {    // Perform transfer logic here}
复制代码

避免长时间占用数据库资源:设置合理的事务超时时间,避免长时间锁定资源。

根据业务需求设置合理的事务超时时间

Spring 事务代码实践与优化

  1. 使用注解式事务管理的规范

  • 优雅地配置 @Transactional:指定 rollbackFor 和 noRollbackFor,明确哪些异常需要回滚,哪些不需要;

  • 避免过度使用事务注解:仅在需要多个数据库操作的场景中使用事务注解,避免不必要的性能开销。

  1. 编程式事务管理的应用及优化

  • 使用 TransactionTemplate 灵活控制事务执行策略;

  • 处理异常及事务回滚:捕获异常后进行事务回滚,将异常转换为 Unchecked 异常后再抛出。

Spring 事务监控与故障排查

  1. 事务监控的重要性:监控事务的执行状态,及时发现性能瓶颈和潜在的事务问题。

  2. 实时监控事务执行状态:通过性能监控工具(如 AppDynamics、New Relic)及时了解事务的运行状况。

  3. 结合日志分析事务问题:利用日志系统(如 ELK)分析事务异常、超时及长时间锁等问题,进一步优化事务策略。

总结和建议

避免文章篇幅过长,所以没有讲的很具体,以下是经验总结:

  1. 合理选择事务传播行为:不同的事务传播行为在执行过程中对资源的使用、事务嵌套和数据库性能产生不同影响。根据具体场景和业务需求选择适当的事务传播行为,能有效实现对事务边界的精细控制。

  2. 设置合适的事务隔离级别:事务隔离级别影响着并发事务之间的可见性和数据一致性。根据业务场景选择合适的隔离级别可以在保证数据安全性的同时,避免不必要的性能损失。

  3. 区分只读事务和非只读事务:对于查询操作,设置只读事务可以降低锁竞争,从而提高数据库的并发性能。在组织代码时,确保写操作和只读事务操作分离,以提高事务处理的效率。

  4. 设定合理的事务超时时间:长时间的事务会导致数据库资源被占用,影响其它事务的执行效率。为事务设置合理的超时时间能确保即使在出现问题时,事务不会长时间占用数据库资源,降低系统的整体性能。

  5. 使用正确的事务管理方式:在编程实践中,根据业务需要选择注解式和编程式事务管理方式。注解式事务适用于简单的场景,而编程式事务则提供更高的灵活性,可以实现精细操控。

  6. 制定异常与回滚策略:合理设定 rollbackFor 和 noRollbackFor,确保事务在发生异常时具备正确的回滚策略。通过异常处理和日志分析,可以及时定位事务故障,从而降低系统失效率。

  7. 监控事务执行情况:通过监控工具实时关注事务执行状态,及时发现性能瓶颈,为事务策略优化提供依据。结合日志分析,进一步跟踪和排查事务相关问题。

通过关注以上这些关键点,在实践中持续优化事务管理策略,有助于实现 Spring 事务优化,使应用能在高并发环境下表现出高效率和低失效的优势。

发布于: 刚刚阅读数: 4
用户头像

xfgg

关注

THINK TWICE! CODE ONCE! 2022-11-03 加入

目前:全栈工程师(前端+后端+大数据) 目标:架构师

评论

发布
暂无评论
Spring事务优化解析:实现高效率、高并发、低失效的全新策略_spring_xfgg_InfoQ写作社区