【问题标题】:H2 - Tomcat jdbc connection pool not reclaiming connections once it hits the max limitH2 - Tomcat jdbc 连接池一旦达到最大限制就不会回收连接
【发布时间】:2018-10-15 09:44:17
【问题描述】:

问题陈述

我们在嵌入式模式下使用 H2 已经有一段时间了。它上面配置了一个连接池。以下是当前池配置:

h2.datasource.min-idle=10
h2.datasource.initial-size=10
h2.datasource.max-active=200
h2.datasource.max-age=600000
h2.datasource.max-wait=3000
h2.datasource.min-evictable-idle-time-millis=60000
h2.datasource.remove-abandoned=true
h2.datasource.remove-abandoned-timeout=60
h2.datasource.log-abandoned=true
h2.datasource.abandonWhenPercentageFull=100

H2 配置:

spring.h2.console.enabled=true
spring.h2.console.path=/h2
h2.datasource.url=jdbc:h2:file:~/h2/cartdb
h2.server.properties=webAllowOthers
spring.h2.console.settings.web-allow-others=true
h2.datasource.driver-class-name=org.h2.Driver

*跳过用户名和密码属性。

我们已经通过记录池属性验证了以上配置生效。

此设置的问题是我们观察到定期(尽管是间歇性的)连接池耗尽,一旦连接池达到最大限制,它就会开始为某些查询抛出以下异常。

SqlExceptionHelper.logExceptions(SqlExceptionHelper.java:129) - [http-apr-8080-exec-38] 超时:池为空。无法在 3 秒内获取连接,无可用 [size:200;忙:200;空闲:0;最后等待:3000]。

此后,即使在我们重新启动 Web 服务器(在本例中为 tomcat)之前,它也无法从该状态恢复。

H2驱动依赖:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
    <scope>runtime</scope>
</dependency>

查询模式和吞吐量

我们使用 h2 为每个请求加载一些数据,然后执行几个(大约 50 个)SELECT 查询,最后删除数据。这导致在 h2 上每分钟(下班时间除外)持续 30k-40k 调用(根据新的遗迹监控)。

每个读取操作都会获取一个新连接,并在执行后释放。

EntityManager entityManager = null;
try {
     entityManager = entityManagerFactory.createEntityManager();
     Query query = entityManager.createNativeQuery(sqlQuery);
     query.setParameter("cartId", cartId);
     List<String> resultList = query.getResultList();
     return resultList;
} finally {
         if(null != entityManager) { entityManager.close(); }
}

观察

  • 应用程序重新启动后,池利用率最低,直到池利用率突然上升并最终达到最大限制。这会在 1-2 天内发生。
  • 一旦池达到最大连接限制,与返回的连接计数相比,借用的连接计数会以更快的速度增加,否则会彼此非常接近。
  • 同时,放弃的连接数也开始随着放弃日志的增加而增加。
  • 有趣的是,查询响应时间在池耗尽后保持不变。所以这种情况排除了慢查询。
  • 即使在流量最少的最奇怪的时间,也会发生此问题。所以它与流量无关。

请指导我们正确的方向来解决这个问题。

更新

最近我们在堆栈跟踪中发现了以下原因,当一个此类事件发生时:

原因:org.h2.jdbc.JdbcSQLException: 数据库可能已经存在 使用:空。可能的解决方案:关闭所有其他连接;使用 服务器模式 [90020-196]

引起:java.lang.IllegalStateException:文件被锁定: nio:/root/h2/cartdb.mv.db [1.4.196/7]

原因:java.nio.channels.OverlappingFileLockException

因此,在深入研究这一点后,我们决定转向内存模式,因为我们不需要在应用程序的生命周期之外保留数据。因此,不应发生文件锁定,从而减少或消除此问题。

无论哪种情况都会回来更新。

【问题讨论】:

  • 您使用的是哪个H2 jdbc驱动版本;这是最新的吗?
  • 我们使用的是 1.4.196。 1.4.197 是截至 2018 年 3 月的最新版本。
  • @MickMnemonic,我似乎找不到最新版本的发行说明,所以不知道版本升级是否能解决问题。
  • @manu change log for version 1.4.197 列出了 1.4.196 的大约 250 个错误修复!因此,如果对您可行,升级似乎是值得的,即使它不一定能解决您的特定问题。
  • 感谢@skomisa 的发行说明。我正在查看他们的 Github 存储库。肯定会这样做。

标签: java tomcat h2 jdbc-pool


【解决方案1】:

自上次更新该问题以来:

在观察了相当长一段时间的性能后,我们得出结论,在文件模式(嵌入式)中使用 H2 会以某种方式导致周期性的文件锁定异常(尽管不规则)。

由于我们的应用程序不需要在应用程序的生命周期之后持久保存数据,因此我们决定转向纯内存模式。

虽然文件锁异常的谜团还需要揭开。

【讨论】:

    猜你喜欢
    • 2010-12-09
    • 2014-12-06
    • 2013-05-01
    • 2015-12-23
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 1970-01-01
    相关资源
    最近更新 更多