如何正确关闭H2?

28
这与这个帖子有关。
我认为我遇到了与 H2 相关的问题,即它无法正确关闭。
我怀疑这是因为在关闭 tomcat 时,我看到了 myDB.lock.db,但进程并未停止。
我使用Tomcat的连接池,数据库的URL为:
url="jdbc:h2:file:/opt/myOrg/tomcat/webapps/MyApplication/db/myDatabase;SCHEMA=myschema"

来自文档关闭 H2:

 

通常,当最后一个连接关闭时,数据库会被关闭。... 默认情况下,当最后一个连接关闭时,数据库会被关闭。但是,如果从未关闭,则在虚拟机使用关闭挂钩正常退出时,数据库将关闭。

我不明白我是否做错了什么。
我应该通过命令强制关闭数据库吗?这就是关闭钩子的含义吗?
我在这里做错了什么?

注意:
我在谷歌上找不到如何正确关闭 H2 的示例(除了它会在最后一个连接关闭时自动关闭的陈述)。我应该自己调用 SHUTDOWN 吗?这是正确的方法吗?
我已经看到有投票关闭问题,但没有给出任何理由或关于我正在调查的示例的链接。

更新:
根据 Joonas Pulakka 的回答,我得到了一些额外的信息:

从使用 kill -3 得到的 javacore 中,我看到了线程:

 

"H2 Log Writer MYAPPLICATION" J9VMThread:0x08DC6F00

j9thread_t:0x08C9B790, java/lang/Thread:0xE7206CC8, 状态:CW, 优先级=5 3XMTHREADINFO1 (本地线程ID:0xA32, 本地 优先级:0x5, 本地策略:未知) 3XMTHREADINFO2
(本地堆栈地址范围从:0xE5E26000, 到:0xE5E67000, 大小:0x41000) 3XMTHREADINFO3 Java调用堆栈:
4XESTACKTRACE at java/lang/Object.wait(Native Method)
4XESTACKTRACE at java/lang/Object.wait(Object.java:196(Compiled Code)) 4XESTACKTRACE at org/h2/store/WriterThread.run(WriterThread.java:102)
4XESTACKTRACE at java/lang/Thread.run(Thread.java:736)

3XMTHREADINFO "pool-8-thread-1" J9VMThread:0x087C0200, j9thread_t:0x0840566C, java/lang/Thread:0xE79BFC80, 状态:P, 优先级=5
3XMTHREADINFO1 (本地线程ID:0xE1A, 本地 优先级:0x5, 本地策略:未知) 3XMTHREADINFO2
(本地堆栈地址范围从:0xE5F69000, 到:0xE5FAA000, 大小:0x41000) 3XMTHREADINFO3 Java调用堆栈:
4XESTACKTRACE at sun/misc/Unsafe.park(Native Method)
4XESTACKTRACE at java/util/concurrent/locks/LockSupport.park(LockSupport.java:184(Compiled Code)) 4XESTACKTRACE at java/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1998(Compiled Code)) 4XESTACKTRACE at java/util/concurrent/LinkedBlockingQueue.take(LinkedBlockingQueue.java:413(Compiled

注释:该文本是有关线程的信息,其中j9thread_t是JVM中的线程ID,java/lang/Thread是Java中表示线程的类,状态表示线程当前所处的状态,优先级表示线程优先级的值。调用堆栈显示了在执行时线程的行为。Code)) 4XESTACKTRACE 在java/util/concurrent/ThreadPoolExecutor.java的958行(已编译代码)获取任务 4XESTACKTRACE 在java/util/concurrent/ThreadPoolExecutor$Worker.java的918行运行 4XESTACKTRACE 在java/lang/Thread.java的736行运行

3XMTHREADINFO "H2文件锁看门狗opt/myOrg/tomcat/webapps/MyApplication/db/myDatabase.lock.db" J9VMThread:0x08DC6900,j9thread_t:0x08C9BA24,java/ lang/ Thread:0xE71E9018,状态:CW,优先级=9 3XMTHREADINFO1
(本地线程ID:0xA30,本地优先级:0x9,本地策略:UNKNOWN)
3XMTHREADINFO2 (本地堆栈地址范围从:0xE5DBA000到0xE5DFB000,大小为: 0x41000) 3XMTHREADINFO3 Java调用堆栈:
4XESTACKTRACE 在java/lang/Thread.sleep(Native Method)中休眠 4XESTACKTRACE
在java/lang/Thread.sleep(Thread.java:851(编译代码))中休眠 4XESTACKTRACE 在org/h2/store/FileLock.run(FileLock.java:490)中运行 4XESTACKTRACE
在java/lang/Thread.java的736行运行

3XMTHREADINFO "文件监视器" J9VMThread:0x087C0800,j9thread_t:0x08C9B4FC,java/lang/Thread:0xE715D878,状态:CW,优先级=5
3XMTHREADINFO1 (本地线程ID:0xA2C,本地优先级:0x5,本地策略:UNKNOWN) 3XMTHREADINFO2
(本地堆栈地址范围从:0xE5E67000到0xE5EA8000,大小为:)

size:0x41000) 3XMTHREADINFO3 Java调用堆栈:
4XESTACKTRACE 在java/lang/Thread.sleep(Native Method)处 4XESTACKTRACE 在 java/lang/Thread.sleep(Thread.java:851(Compiled Code))处 4XESTACKTRACE 在org/apache/log4j/helpers/FileWatchdog.run(FileWatchdog.java:104)处


可能是Play!没有正确关闭H2的重复问题。 - Mohamed Mansour
@MohamedMansour:我已经阅读了那个线程,但没有帮助。1)我关闭的是Tomcat而不是应用程序。因此,不应该有任何连接处于打开状态。2)答案似乎是一个解决方法,我正在尝试理解通过挂钩使用SQL命令强制“关闭”是否实际上是推荐的方法。我无法从文档中确定。 - Jim
H2会为您调用addShutdownHook(),并使用org.h2.engine.DatabaseCloser的实例。 - trashgod
@trashgod:那么我不应该自己做,例如使用钩子并执行SQL关闭吗?我在Google或H2文档中找不到有关如何关闭H2的示例。 - Jim
1
我不理解“我关闭了Tomcat,但进程没有停止”。这是否意味着Tomcat无法正确关闭?如果是这样,您应该找出Tomcat无法关闭的原因。 - user330315
1
@a_horse_with_no_name:从catalina.out中没有错误。并且在运行进程上使用kill -3,我从ps -ef中看到H2仍然是活动的。此外,我还看到了H2db.lock。因此,我强烈怀疑这是H2的问题,而不是Tomcat的问题。 - Jim
5个回答

9
文档说明H2数据库连接在虚拟机正常退出时会关闭,实际也是这样的。默认已经有了关闭钩子,您不需要做任何事情。关闭钩子是关闭只需要在退出时关闭的资源的一种有效方式。
如果在关闭后仍然存在.lock.db文件,则说明虚拟机没有正常退出。您写道该进程不会停止。您必须找到原因,因为很可能这也阻止了H2关闭钩子的执行。
对于大型数据库,关闭可能需要一些时间。在调试器中查看(例如VisualVM),在您调用(Tomcat)关闭后哪些线程仍然活动。
还有一种可能性:文件权限设置为H2可以创建锁定文件,但无法删除它们。如果操作系统防止H2删除其锁定文件,则H2无能为力。

谢谢。我正在尝试调查进程为什么不停止(https://dev59.com/Cmkw5IYBdhLWcg3wVpBi),最终到了这里。此外,在文件夹中,用户具有`rw-`权限,组具有`r--`权限。我需要`x`权限才能删除吗? - Jim
x 只用于执行,而不是删除。但要检查目录是否设置了粘滞位 - Joonas Pulakka
@Jim:你并没有卡住。你只是排除了权限问题的可能性(即使它确实是一个权限问题,也只能解释.lock.db文件,而不能解释进程为什么不停止。(如果H2无法删除文件,则无法删除,但它绝对不会因此永远阻塞)。)当你调用Tomcat关闭时,哪些线程会一直在做某些事情? - Joonas Pulakka
我已经在这方面更新了OP。我也看到一些不是我的线程,但我认为我应该专注于我的线程。让我怀疑的部分是H2Log4j。其他的线程我不确定。 - Jim
有任何带有3XMTHREADBLOCK的代码行吗?例如,H2 Log Writer正在等待某些东西,但具体是什么呢?您必须关注线程所形成的任何"网络"。 - Joonas Pulakka
显示剩余4条评论

6

通过查看DbStarter.contextDestroyed()的代码(感谢Allan5答案),以下是可行的代码:

connection.createStatement().execute("SHUTDOWN");

所以Aaron Digulla答案是正确的(即使不能完全“复制/粘贴”)。

此外,如果您使用server = Server.createTcpServer("-tcpAllowOthers")启动了H2 TCP服务器,则可以使用server.stop()简单地停止它。


6
你可以执行语句SHUTDOWN,然后关闭连接。 SHUTDOWN命令会立即释放与连接相关的所有H2资源。例如,这将允许您在重新部署Web应用程序时摆脱嵌入式H2数据库。

3

不确定这是否与您的情况相关,但您是否尝试添加DBStarter监听器?

http://www.h2database.com/html/tutorial.html,请参见“使用Servlet监听器启动和停止数据库”部分。

该链接建议在web.xml中添加以下内容:

<listener>
    <listener-class>org.h2.server.web.DbStarter</listener-class>
</listener>

请参考此处的讨论(虽然是2008年的,可能已经过时)- 显然修复程序适用于嵌入式和非嵌入式实例:http://groups.google.com/group/h2-database/browse_thread/thread/eb609d58c96f4317 或者,您是如何使用连接的? 您确定您是否正确清理了连接?
我之前遇到过问题,我的情况是我正在使用带有JPA EntityManager的连接,并且在使用后忘记关闭EntityManager实例,这导致了一些问题:
@PersistenceUnit(unitName="myEm")
private EntityManagerFactory emf;

public void doStuff() {
    EntityManager em = emf.createEntityManager();
    ...
    em.close(); // forgot this line
}

1
不,关闭挂钩只是一个线程,在JVM终止时运行,无论是通过从main()返回、调用System.exit(int)还是抛出异常。只有JVM崩溃才会避免它。请参见Runtime.addShutdownHook(Thread)。

我知道什么是关闭挂钩。但我无法确定这是否是关闭H2的正确方式。 - Jim
@Jim:如果你知道什么是关闭钩子,为什么还要问“这是关闭钩子的意思吗? - user330315
1
@a_horse_with_no_name: 我的意思是在关闭 H2 的情况下。我找不到一个示例来显示我应该自己添加钩子。 - Jim
我从这份文档中得到的信息是,关闭挂钩是由框架提供的,您不需要添加它。 - Christian Schlichtherle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接