首页 > 解决方案 > JdbcSQLNonTransientConnectionException:数据库可能已在使用中:“等待数据库关闭时间超过 1 分钟”

问题描述

我们使用 H2 作为数据库服务器进程启动并侦听标准 TCP/IP 端口 9092。

我们的应用程序使用连接池部署在 Tomcat 中。我们在空闲时间进行清除,最终导致关闭与 H2 的所有连接。当应用程序尝试再次打开与 H2 的连接时,我们有时会观察到错误:

SCHEDULERSERVICE schedule: Exception: Database may be already in use: "Waited for database closing longer than 1 minute". Possible solutions: close all other connection(s); use the server mode [90020-199]
org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database may be already in use: "Waited for database closing longer than 1 minute". Possible solutions: close all other connection(s); use the server mode [90020-199]
       at org.h2.message.DbException.getJdbcSQLException(DbException.java:617)
       at org.h2.message.DbException.getJdbcSQLException(DbException.java:427)
       at org.h2.message.DbException.get(DbException.java:205)
       at org.h2.message.DbException.get(DbException.java:181)
       at org.h2.engine.Engine.openSession(Engine.java:209)
       at org.h2.engine.Engine.createSessionAndValidate(Engine.java:178)
       at org.h2.engine.Engine.createSession(Engine.java:161)
       at org.h2.server.TcpServerThread.run(TcpServerThread.java:160)
       at java.lang.Thread.run(Thread.java:748)

       at org.h2.message.DbException.getJdbcSQLException(DbException.java:617)
       at org.h2.engine.SessionRemote.done(SessionRemote.java:607)
       at org.h2.engine.SessionRemote.initTransfer(SessionRemote.java:143)
       at org.h2.engine.SessionRemote.connectServer(SessionRemote.java:431)
       at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:317)
       at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:169)
       at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:148)
       at org.h2.Driver.connect(Driver.java:69)
       at java.sql.DriverManager.getConnection(DriverManager.java:664)

当 Tomcat 连接池关闭所有空闲连接(未使用)并且一个仍在使用的连接随后关闭时,就会出现此问题。

下一次尝试打开新连接失败,等待一段时间后重试成功。

在我看来,H2 在关闭最后一个连接后会关闭数据库。

提前谢谢

标签: javah2

解决方案


Web 应用程序中的嵌入式数据库需要仔细处理其生命周期。

您可以添加一个javax.servlet.ServletContextListener实现(标有@WebListener注释或包含在中web.xml)并将显式数据库关闭添加到其contextDestroyed()方法。

您可以在此处使用 强制关闭数据库connection.createStatement().execute("SHUTDOWN")。如果您的应用程序需要在卸载期间向数据库写入内容,则应在该命令之前执行。

如果未显式配置某些其他行为(例如,使用 JDBC URL 中的参数),则没有显式关闭 H2 会在所有连接关闭时关闭数据库。例如,DB_CLOSE_DELAY设置额外的延迟,也许您的应用程序使用该设置,因此 H2 不会立即关闭数据库,或者应用程序不会立即关闭所有连接。

无论如何,当您尝试更新 fly 的 Web 应用程序时,Tomcat 会尝试在卸载旧版本之前初始化新版本。如果H2在web应用本身的classpath中,新版本已经上线但旧版本还没有卸载的情况下,新版本会在短时间内无法连接到数据库。

如果您不喜欢它,您可以运行独立的 H2 Server 进程并在您的 Web 应用程序中使用远程连接。

另一种选择是将 H2 移动到 Tomcat 本身的类路径中,并将连接池配置为 中的资源server.xml,在这种情况下,它不应受到应用程序生命周期的影响。

在这两种情况下,您都不应该使用该SHUTDOWN命令。


更新

对于远程服务器的客户端-服务器连接,这种异常意味着服务器决定关闭数据库,因为没有活动连接。此操作中途不能中断和还原。在此过程中尝试打开与同一数据库的新连接时,它最多等待 1 分钟以完成此过程以再次重新打开数据库。此超时不可配置。

有两种可能的解决方案。

  1. DB_CLOSE_DELAY设置可以在几秒钟内使用一些较大的值。当所有连接都关闭时,数据库将保持在线指定的秒数。-1也可用于设置无限超时。

  2. 您可以尝试加快关机过程,但您必须自己弄清楚什么需要这么多时间。文件压缩过程默认限制为200毫秒,可能需要更长的时间,但我认为不应该那么长。也许您有很多临时对象或未提交的数据。也许你有一个非常高的数据库文件碎片。如果没有进一步调查,很难说出了什么问题。


推荐阅读