写点什么

Spring 避坑指南:Spring 声明式事务 @Transactional 避坑

作者:崔认知
  • 2022 年 8 月 28 日
    北京
  • 本文字数:1700 字

    阅读完需:约 6 分钟

Spring避坑指南:Spring声明式事务@Transactional避坑

简介


首发地址:https://mp.weixin.qq.com/s?__biz=Mzg4MzcwMTk0Mw==&mid=2247484187&idx=1&sn=e127ec225b410e85b3e7b4740ddb6eae&chksm=cf4222c3f835abd5a2272789054b69249ce6bd1c1ff4d47882569194bc9328b2087f10c58603&token=765340310&lang=zh_CN#rd


Spring 支持两种使用事务的方式:声明式和编程式。声明式事务是大多数程序员使用的,一个注解 @Transactional 走天下,由于事务的特性及事务是由 aop 技术来实现的,往往会碰到一些坑,使得事务失效或性能受损,甚至发生死锁现象。


事务失效的坑:AOP 技术限制引起的



Spring 中的事务是 AOP 实现的,Srping AOP 使用 JDK 动态代理或 CGLIB 来创建代理对象。


默认情况下,如果需要代理的对象实现了接口,则使用 JDK 动态代理,否则使用 CGLIB。



可以参考文档:

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying


或源码:


org.springframework.aop.framework.DefaultAopProxyFactory
复制代码




由于 JDK 动态代理或 CGLIB 来创建代理技术限制,某些方法或行为不能创建代理行为或自动使用代理对象调用方法,会使得事务失效。


1、final 方法添加 @Transactional,事务不生效;


2、static 方法添加 @Transactional,事务不生效;


3、非 public 方法添加 @Transactional,事务不生效;


可以通过 class-based proxies or consider using AspectJ compile-time or load-time weaving 解决。


4、同一个类的带有事务注解 @Transactional 的两个方法 self-invocation 行为,事务不生效;


jdk 动态代理技术肯定失效,可以通过 CGLIB 技术规避。


事务的坑:Spring 实现机制引起的



1、抛出受检异常 Exception 无法回滚


默认情况下,只有非受检异常 RuntimeException、Error 发生时,事务才会回滚。受检异常 Exception 发生时不会回滚。



图片来源:https://javadevcentral.com/checked-and-unchecked-exception-in-java



事务回滚源码:


org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing
复制代码



默认情况下,非受检异常会回滚



我们可以设置回滚 Exception 异常类型,来解决受检异常不回滚的问题:


    @Transactional(rollbackFor=Exception.class)
复制代码


处理逻辑如下:


org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
复制代码



2、子事务出异常回滚当前事务,导致父方法也无法提交事务


事务的默认传播行为为 Propagation.REQUIRED,子事务可以设置 Propagation.REQUIRES_NEW,在独立事务中执行


3、方法内 try catch 异常,不再抛给事务框架,不会回滚事务


自己吞掉了异常,Spring 框架不会探测到异常


4、事务多个业务有异步执行,异常不抛出,事务不会回滚


事务的实现涉及到 java 的 ThreadLocal 特性,如果异步执行,事务信息丢失或异常丢失,导致事务执行或回滚。



来源:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#tx-decl-explained


5、一个事务中多个业务有同步或异步执行,使用不同的数据源,事务不会生效


使用 spring 的本地事务,同一个事务内必须一个数据源,不能跨数据源,否则必须使用分布式事务。



来源:https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction-local


6、事务所在的类不是 spring 容器管理的


7、未配置事务管理器


8、其他 aop 顺序问题,并且吞并异常,事务失效


事务 aop 有自己默认的顺序:


org.springframework.transaction.annotation.EnableTransactionManagement#order
复制代码



如果其他开发者或者框架引入的 aop 顺序和事务的顺序相同,由于 Spring 框架 aop 排序问题,很可能导致一些问题的发生。


事务的坑:数据库引起的



1、数据库引擎不支持事务

事务的坑:大事务引发问题



1、锁定数据太多,容易造成大量阻塞或死锁问题和锁等待时间长而引发的锁超时问题;


2、回滚记录占用大量存储空间,事务回滚时间长;


3、并发情况下数据库连接处被占满;


4、事务执行时间长,事务结束后才写入 binlog,容易造成数据库主从延迟


如何避免大事务:


1、不要一股脑的用 @Transactional 注解;


2、大事务拆分为独立的小事务;


3、事务避免 PRC 调用-分布式事务;


4、事务中避免一次处理太多的数据;


5、能不用事务就不用;


小结




发布于: 1 小时前阅读数: 10
用户头像

崔认知

关注

认知科技技术团队-微信公众号 2021.07.30 加入

认知科技技术团队

评论

发布
暂无评论
Spring避坑指南:Spring声明式事务@Transactional避坑_Spring避坑指南_崔认知_InfoQ写作社区