在h2中尝试锁定表时出现超时错误

50

在某种情况下,当不同的线程通过批量上传操作填充大量用户并且我尝试查看另一个网页上的所有用户列表时,列表查询会抛出以下超时错误。是否有一种方法可以设置此超时时间,以避免出现超时错误。

环境:h2(最新版本),Hibernate 3.3.x

Caused by: org.h2.jdbc.JdbcSQLException: Timeout trying to lock table "USER"; SQL statement:

[50200-144]

    at org.h2.message.DbException.getJdbcSQLException(DbException.java:327)
    at org.h2.message.DbException.get(DbException.java:167)
    at org.h2.message.DbException.get(DbException.java:144)
    at org.h2.table.RegularTable.doLock(RegularTable.java:482)
    at org.h2.table.RegularTable.lock(RegularTable.java:416)
    at org.h2.table.TableFilter.lock(TableFilter.java:139)
    at org.h2.command.dml.Select.queryWithoutCache(Select.java:571)
    at org.h2.command.dml.Query.query(Query.java:257)
    at org.h2.command.dml.Query.query(Query.java:227)
    at org.h2.command.CommandContainer.query(CommandContainer.java:78)
    at org.h2.command.Command.executeQuery(Command.java:132)
    at org.h2.server.TcpServerThread.process(TcpServerThread.java:278)
    at org.h2.server.TcpServerThread.run(TcpServerThread.java:137)
    at java.lang.Thread.run(Thread.java:619)
    at org.h2.engine.SessionRemote.done(SessionRemote.java:543)
    at org.h2.command.CommandRemote.executeQuery(CommandRemote.java:152)
    at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:96)
    at org.jboss.resource.adapter.jdbc.WrappedPreparedStatement.executeQuery(WrappedPreparedStatement.java:342)
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1808)
    at org.hibernate.loader.Loader.doQuery(Loader.java:697)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
    at org.hibernate.loader.Loader.doList(Loader.java:2228)
    ... 125 more
8个回答

50

是的,你可以更改锁定超时时间。默认值相对较低:1秒(1000毫秒)。

在许多情况下,问题是另一个连接已锁定了表格,并且使用多版本并发也可以解决问题(在数据库URL上附加 ;MVCC=true )。

编辑:MVCC = true参数不再受支持,因为自h2 1.4.200以来,它对于MVStore引擎始终为真,这是默认引擎。


8
使用参数“MVCC=true”是解决方法,改变超时时间无法解决此问题。 - Bryan Hunt
1
@BryanHunt 是的,MVCC=true 也可以解决这个问题,我已经更新了我的答案。然而,这个问题是关于锁超时的,增加锁超时在许多情况下也是一种解决方案。如果它不是你的用例的解决方案,那没关系,但问题根本没有包括用例,所以这取决于具体情况。 - Thomas Mueller
@ThomasMueller:如果我正在使用内存数据库,那么应该在哪里或如何设置“mvcc=true”?此外,由于我正在使用数据源中的**jdbc:embedded-database**标记,因此在配置中不使用任何URL。 - arun_kk
5
@ThomasMueller,我在H2数据库上遇到了同样的问题。我尝试在连接URL中加入MVCC=true,但没有起到帮助作用。 - Gobliins
1
@Sen 不行,因为对同一行的并发修改会被拒绝(只有一个事务会成功)。 - Thomas Mueller
显示剩余8条评论

39

1
这是正确的答案,改变锁定超时时间不是解决办法。 - Bryan Hunt
4
@BryanHunt,这取决于具体情况。你只能说这不是适用于你的使用场景的解决方案。 - Thomas Mueller
@ThomasMueller 如果我们使用“MVCC=true”,会有什么缺点吗? - Roshan Wijesena
1
@RoshanWijesena 是的,在 H2 版本 1.3.x(使用 PageStore)中,MVCC 模式测试不够充分。然而,在版本 1.4.x 中(使用 MVStore),它是默认模式。 - Thomas Mueller
6
我正在使用默认情况下MVCC=true的H2 DB 1.4.x。但我仍然遇到错误。 - jprism

7
如果你遇到这个错误,我建议你不要在批量数据库操作上使用事务。相反,考虑在每个单独的更新上使用一个事务:将整个批量导入视为一个事务是否有意义?可能不是。如果是,则MVCC=true或更大的锁定超时是合理的解决方案。
然而,我认为大多数情况下,你看到这个错误是因为你试图执行一个非常长的事务——换句话说,你没有意识到你正在执行一个真正的长事务。这对于我本人来说确实是这样,我只需更加注意如何编写记录(使用无事务或使用较小的事务),就可以解决锁超时问题。

6

对于那些在集成测试中遇到问题的人(例如服务器正在访问h2数据库,而集成测试在调用服务器之前访问数据库以准备测试),在测试之前执行的脚本中添加一个“commit”可以确保在调用服务器之前数据已经存在于数据库中(不使用MVCC = true - 如果不启用则我认为有点“奇怪”,因为它不是默认启用的)。


3

我在连接字符串中设置了MVCC=true,但仍然出现上述错误。我添加了;DEFAULT_LOCK_TIMEOUT=10000;LOCK_MODE=0,问题得到解决。


2
在H2数据库URL中,"LOCK_MODE=0"的意思是“使用未提交读”,听起来不太好。 - Krzysztof Tomaszewski

1
使用DBUnit、H2和Hibernate - 出现同样的错误,MVCC = true有所帮助,但在删除数据后仍会出现错误。解决这些情况的方法是将实际删除代码包装在事务中:
Transaction tx = session.beginTransaction();
...delete stuff
tx.commit(); 

1

我在使用PlayFramework时遇到了问题

JPAQueryException出现:在查询models.Page中的name=?时发生错误:尝试锁定表“PAGE”超时

这实际上是一种无限循环,因为我有一个

@Before

没有unless,导致函数反复调用自身

@Before(unless="getUser")


1

来自2020年的用户,参见reference

基本上,该参考文献表示:

设置当前会话的锁定超时时间(以毫秒为单位)。此设置的默认值为1000(一秒)。

此命令不提交事务,回滚不受影响。可以将此设置附加到数据库URL中:jdbc:h2:./test;LOCK_TIMEOUT=10000


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