数据库连接使用问题 - 1

原理       

 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());
}

相关推荐

  1. 数据库连接使用问题 - 1

    2024-01-08 03:38:01       58 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-08 03:38:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-08 03:38:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-08 03:38:01       82 阅读
  4. Python语言-面向对象

    2024-01-08 03:38:01       91 阅读

热门阅读

  1. Docker学习笔记(一):Docker命令总结

    2024-01-08 03:38:01       66 阅读
  2. 学习录

    学习录

    2024-01-08 03:38:01      51 阅读
  3. 【深度学习在时序数据异常检测中的创新】

    2024-01-08 03:38:01       51 阅读
  4. tyxsspa/AnyText 阿里生成文字

    2024-01-08 03:38:01       62 阅读
  5. unittest框架管理测试用例

    2024-01-08 03:38:01       57 阅读
  6. Iceberg: 列式读取Parquet数据

    2024-01-08 03:38:01       72 阅读
  7. 阿里云服务器CPU内存配置怎么选择?

    2024-01-08 03:38:01       89 阅读
  8. 何为算法之什么是算法

    2024-01-08 03:38:01       49 阅读