数据库连接使用问题 - 1

发布时间:2024年01月07日
原理? ? ? ?

?open-in-view 是 Spring Boot ?动加载 Spring Data JPA 提供的?个配置,全称为 spring.jpa.open-in-view=true,它只有 true 和 false 两个值,默认是 true。
????????这个配置为true时,会导致Web MVC请求处理的一开始,就从连接池取一个数据库连接放到本地线程变量;Web MVC请求处理结束后,才会归还给连接池
open-in-view 机制是为了解决在 mvc 的 controller 中使用了 hibernate 的 lazy load 的属性时 no session 抛出的LazyInitializationException 异常

????????这个处理逻辑位于OpenEntityManagerInViewInterceptor.preHandle,在Sentinel处理代码SentinelResourceAspectExt之前,所以Sentinel限流无法阻止JPA取数据库连接。
?

隐患一


????????如果业务代码中,有耗时操作,就导致数据库连接还回连接池变慢,进而引起雪崩效应,导致tomcat处理线程大量增加,而处理性能却无法提高,所有Web MVC请求都无法响应,应用提供的服务中断。

@ResponseBody
@RequestMapping(value = "/xxx/send", method = RequestMethod.POST)
@SentinelResource(value = "xxxdcc#send", entryType = EntryType.IN)
public SendXXXResp sendSms(@RequestBody SendXXXVo sendXXXVo) {
    //同步调用第三方系统,耗时超过一定时间,如3s
    SendXXXResp sendXXXResp = new SendXXXResp();
    return sendXXXResp;
}

????????如果数据库连接池最大连接数为200,同步调用第三方系统耗时3s。那么这个接口的QPS达到70时,会导致应用雪崩,整个应用无法提供服务。

隐患二

? ? ? ? 当jpa与mybatis等框架混合使用时,存在死锁的问题。一个Web MVC请求处理时,JPA框架会从连接池中取一个数据库连接放入本地线程变量,业务处理代码使用mybatis访问数据库,也需要取一个数据库连接,在极端情况下,取不到就会等待,导致JPA取的连接也没有还给连接池,导致死锁。
也会导致tomcat处理线程大量增加,而处理性能却无法提高,所有Web MVC请求都无法响应,应用提供的服务中断。
示例代码:

@ResponseBody
@RequestMapping(value = "/xxx/send", method = RequestMethod.POST)
@SentinelResource(value = "xxx#send", entryType = EntryType.IN)
public SendXXXResp sendSms(@RequestBody SendSmsVo sendSmsVo) {
    //使用mybatis框架访问数据库
    CallingConfig callingConfig=callingConfigMapper.selectById(sendXXXVo.getXXXAccount());
    
    SendXXResp sendXXXResp = new SendXXXResp();
    return sendXXXResp;
}

????????如果数据库连接池最大连接数为200,这个接口QPS达到200。即使方法体中没有耗时操作,也会导致应用雪崩,整个应用无法提供服务。

修改建议
  1. 将spring.jpa.open-in-view配置为false,注意不要设置成了“spring.shardingsphere.jpa.open-in-view”
  2. 不要使用JPA 的关联查询,如不要使用@OneToMany等关联查询的注解。以免open-in-view为false时,出现LazyInitializationException 异常。

annel_account_config账号表与calling_config主叫号码表是一对多的关系;

JPA中设置的关联查询:

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "channelAccountId")
private List<CallingConfigInfo> callingConfigList = new ArrayList<>();

在Controller中进行了查询使用:

ChannelAccountConfigInfo channelAccountConfigInfo= channelAccountConfigRepository.findOne(1L);
List<CallingConfigInfo> callingConfigList = ObjectUtil.getOptional(()->channelAccountConfigInfo
        .getCallingConfigList()).orElse(new ArrayList<>());
for(CallingConfigInfo callingConfigInfo:callingConfigList){
    log.info("Calling:"+callingConfigInfo.getCalling());
}

文章来源:https://blog.csdn.net/cqupt2012214390/article/details/135438653
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。