尝试使用Spring / JPA / Hikari在我的代码中查找数据库连接泄漏

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尝试使用Spring / JPA / Hikari在我的代码中查找数据库连接泄漏相关的知识,希望对你有一定的参考价值。

我有一个Spring Web应用程序的问题,它定期遇到从我的连接池获取连接时出错。最后在日志中我看到如下条目:

  • 引起:javax.persistence.PersistenceException:org.hibernate.exception.JDBCConnectionException:无法获取JDBC连接
  • 引起:java.sql.SQLTransientConnectionException:HikariPool-1 - 连接不可用,请求在30000ms后超时。

只有恢复的方法我发现它一旦遇到这一点就重启Tomcat。

我认为最可能的解释是我在某处某些代码没有正确清理它的连接 - 将其返回给Hikari,留下一些东西让Spring无法清理它等等。

要解决问题,我已将hikari配置leakDetectionThreshold设置为5000毫秒并启用了日志记录。之后,我看到日志条目如

2018-04-24 19:53:56 WARN  ProxyLeakTask:87 - Connection leak detection 
triggered for org.postgresql.jdbc.PgConnection@664ec666, stack trace 
follows
java.lang.Exception: Apparent connection leak detected
    at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
    at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:35)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:99)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:129)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:47)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:146)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:148)
    at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1940)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1909)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1887)
    at org.hibernate.loader.Loader.doQuery(Loader.java:932)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
    at org.hibernate.loader.Loader.doList(Loader.java:2615)
    at org.hibernate.loader.Loader.doList(Loader.java:2598)
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2430)
    at org.hibernate.loader.Loader.list(Loader.java:2425)
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:335)
    at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2129)
    at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:981)
    at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:147)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1398)
    at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1444)
    at sun.reflect.GeneratedMethodAccessor191.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:379)
    at com.sun.proxy.$Proxy163.getSingleResult(Unknown Source)
    at com.mycompany.web.jpa.util.DBHelper.getPagedMappedDbResults(DBHelper.java:76)
    at com.mycompany.web.jpa.repository.TaskRepositoryImpl.findTaskDetailsByStepIdAndIdIn(TaskRepositoryImpl.java:245)
......

所以它正在检测可能的泄漏。我猜想可能是假阳性?但这也是我的应用程序中唯一一个在Spring应用程序中经常使用的标准服务/存储库模式之外进行数据库访问的类,因此它似乎是一个可能的罪魁祸首,而且它是我目前最好的领导者。

无论如何,我在跟踪中看到的最后一段非库代码(即我写的东西,所以最有可能是泄漏的原因!)是我的DBHelper :: getPagedMappedDbResults方法,相关位包括在这里:

    Query q = entityManager.createNativeQuery(countQueryText);
    setQueryParameters(q, parameters);
    long numActualResults = 0;
    try {
        numActualResults = ((Number)q.getSingleResult()).longValue(); // line 76
    } catch (Exception e) {
        System.out.println("just in case: " + e);
    }

所以基本上我从我的EntityManager实例创建一个Query对象,设置一些参数,并运行它来获得一些结果。

当我完成它时,我是否需要使用Query对象? q.cleanup()?我从阅读文档中看不到这样的东西,但是我没有在这个资源上做好管家吗?

entityManager本身是从@Autowired注释创建的。我的理解是,如果我没有“新”它来实例化它,而是让Spring框架自动装配它,那么Spring将做任何必要的清理工作。是对的吗?或者在使用entityManager后是否需要进行一些清理?

版本细节:

  • Tomcat 8 / Java 8
  • Spring 5.0.0.RELEASE
  • Spring Data Kay-RELEASE
  • Hibernate 5.2.3.Final
  • Hikari 2.4.5

任何建议或建议将不胜感激,谢谢!

答案

什么是查询?它很重吗?也许你在这里遇到僵局?连接管理看起来很好。您没有明确获取连接,因此无需释放它。查询可能会长时间运行,因此Hibernate无法完成它并释放连接。

此外,您可以检查数据库端的打开连接数。在这方面做一些分析。

以上是关于尝试使用Spring / JPA / Hikari在我的代码中查找数据库连接泄漏的主要内容,如果未能解决你的问题,请参考以下文章

试图在我的代码中找到数据库连接泄漏,使用 Spring / JPA / Hikari

Spring Boot JPA Repository 未释放 Hikari DB Connection

带有 Hikari 的 Spring Data JPA:为啥 hikari 自动提交属性设置为“true”?

Spring Boot : ORM 框架 JPA 与连接池 Hikari

Spring boot Oracle JPA 设置 QueryTimeout

如果不存在,则通过将 spring Jpa 与 hibernate 一起使用来创建模式