事务使用不当引发的线上问题
某天中午正在吃饭时候,很多业务人员在群里反馈服务出问题了,于是赶紧回到公司查看情况,发现后台日志报大量的请求超时,于是马上在控制台查看服务是正常的,结果是服务是正常运行的,然后又看了数据库,数据库也是正常的。接口超时的原因都是同一个:调用另一个服务的某个接口出现超时导致的,于是查看所在的服务是否正常,结果服务也是正常运行的,说明服务存在假死,日志报了大量获取数据库连接失败,报错内容如下,提示是没法获取数据库连接:
org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connect
赶紧临时加大了数据库连接池配置,重启服务后恢复正常了。但是这个已经是双十一过后了,没有那么大的访问压力,于是继续排查原因,发现最近使用比较多只有文件上传,发现上传文件接口也同样报了大量超时,上传文件使用的是三方服务,与服务商联系说是他们的服务被攻击了,已经恢复正常了,但是上传文件的逻辑是上传完成会在数据库里面记录一条文件相关的信息,占用数据库连接的时间很短,直觉告诉我这里面有问题。去看了上传文件那块的代码,发现在上传文件的类上面有个事务注解,也就是整个上传文件的过程都在事务里面执行的,之所以这么久没出问题,是因为上传文件时间一般很短。下面是还原整个过程:
上传文件带了事务->上传文件超时->数据库连接迟迟无法释放->阻塞其他接口建立数据库连接
于是让相关的同事紧急修复了代码,只对插入数据库那段代码加上事务。并且对项目中的事务处理做了一次全面排查,避免类似的问题出现。
造成“org.hibernate.exception.GenericJDBCException: Unable to acquire JDBC Connect”的三种原因及解决方法
1.数据库连接池连接配置太小
解决方法:加大连接池的配置,springboot默认的连接是10个,下面是hikari连接池的配置,其他连接池的配置类似
hikari:
maximum-pool-size: 100
minimum-idle: 20
2.事务使用不当导致连接迟迟无法释放,比如http请求或者一些非常耗时的操作放在事务里面执行
解决方法:http请求或者耗时操作放在事务外面,需要操作数据库的部分放在事务里面执行
3.使用 Propagation.REQUIRES_NEW导致事务相互等待造成的,比如在一个事务中开启另一个事务,可以模拟这种场景:
将连接池最大连接设置为2,发起两个请求,则这两个请求刚好把连接池的连接用完了,当在原来事务中开启新的事务时,则这两个请求都被阻塞住了,直到其中一个请求获取连接超时报错为止
解决方法:对事务做适当拆分,避免使用在一个事务中开启另一个事务,如果一定要这么用,可调大连接池数量,但是不能根本避免这个问题。还有一种解决方法:就是开启新的事务和现有的事务不要使用同一个连接池,这种方案没试过