Spring事务超时探讨

先抛出一个问题,如下,Spring事务的默认超时时间是多少?

图片

先来看如下代码,方法上加上了事务注解,并设置事务超时时间为2s。两者的区别是一个是在插入之前Sleep了3秒,一个是在插入之后Sleep了3秒。测试时发现,前者会抛事务超时异常,而后者则正常插入了,这是为什么呢?

图片

图片

如上,insertStuTimeOut1方法抛出了事务超时异常。我们可以观察它异常栈信息,来分析它是如何一步步走到超时异常的。

  1. 我们知道,Mybatis执行SQL是将SQL封装成一个Statement去执行。如下这边根据方法名称知道它是在准备Statement,而在这里它调用了SpringManagedTransaction的getTimeout方法,这里面会检测事务是否超时了。(Mybatis在3.4.0版本及以上才会去检测Spring事务是否超时,https://github.com/mybatis/spring/issues/115)

图片

图片

图片

  1. 现在来看下为什么insertStuTimeOut1方法超时了,而insertStuTimeOut2方法没有超时?原因刚刚也提到了,Mybatis在执行SQL前会去检测事务是否超时?关键是两者Sleep的时间,insertStuTimeOut2方法中SQL执行时还没有Sleep,所以自然不会超时了。如下图所示,Mybatis是先生成Statement,在生成Statement时就会去检测是否超时。然后才会去执行SQL。

图片

  1. 步骤1中有一个关键的变量deadline,顾名思义,它是截止日期,如果当前似乎超过了deadline,说明超时了需要回滚。那么deadline是在哪边设置的呢?它是在创建事务的时候设置的。

图片

图片

  1. 现在来回答文章开头的问题,Spring默认的事务超时时间是多少?这是@Transactional注解中对事务超时的默认值设置,默认是-1。

图片

在步骤3) 中创建事务给事务设置超时时间时,会调用determineTimeout方法来计算事务超时时间,如果是默认的则返回defaultTimeout,也就是-1。

图片

那么在这一步就不会初始化deadline变量,也就是说deadline变量为null。

图片

而在这一步getTimeout中会去调用holder.getTimeout方法判断deadline是否为null,如果为null,则不会去检测事务是否超时。也就是说,如果不手动设置@Transactional的timeout属性,Mybatis是不会去检测Spring事务是否超时的。

图片

public boolean hasTimeout() {   
	return (this.deadline != null);
}

思考分析,Mybatis这样检测Spring事务是否超时有没有什么问题?它在SQL执行前去执行SQL超时判断?如果SQL执行很长时间,则Mybatis不会认为它是超时的而去回滚它。这本质上是一种同步检测机制。而Hystrix启动单独的线程去检测超时,线程启动的时间刚好就是任务超时的时间,这是一种异步检测机制。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值