表“SYS”中的并发更新:h2 数据库在插入多条记录时失败

Posted

技术标签:

【中文标题】表“SYS”中的并发更新:h2 数据库在插入多条记录时失败【英文标题】:Concurrent update in table "SYS": h2 database fails while inserting multiple records 【发布时间】:2018-04-02 14:07:02 【问题描述】:

我在使用 h2 数据库 1.4.197 时遇到以下错误。基本上,我会尝试在处理记录后立即更新记录。

我不使用插入,而是使用合并语句,因为我不想重复记录。以下是我的查询:

merge into tableName key(col1,col2) values(?,?,?);

我正在使用最大连接池设置为 100 的连接池,并且通过调试,我发现错误是按随机顺序抛出的。话虽如此,插入第 1000 条记录时首先引发错误。在第二次执行时,它发生了第 554 条记录。

这是我的连接字符串:

jdbc:h2:file:" + DB_NAME + ";MV_STORE=true;MVCC=true;AUTO_SERVER=TRUE

下面是堆栈跟踪。

org.h2.jdbc.JdbcSQLException: General error: "java.lang.RuntimeException: Unexpected code path" [50000-197]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:357)
    at org.h2.message.DbException.get(DbException.java:168)
    at org.h2.message.DbException.convert(DbException.java:307)
    at org.h2.message.DbException.toSQLException(DbException.java:280)
    at org.h2.message.TraceObject.logAndConvert(TraceObject.java:357)
    at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:268)
    at com.verscend.dxcg.reports.db.h2.QueryHelper.insertOrUpdateTable(QueryHelper.java:20)
    at com.verscend.dxcg.audit.AuditLogGenerator.process(AuditLogGenerator.java:22)
    at org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:103)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:62)
    at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:145)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.component.seda.SedaConsumer.sendToConsumers(SedaConsumer.java:298)
    at org.apache.camel.component.seda.SedaConsumer.doRun(SedaConsumer.java:207)
    at org.apache.camel.component.seda.SedaConsumer.run(SedaConsumer.java:154)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.RuntimeException: Unexpected code path
    at org.h2.message.DbException.throwInternalError(DbException.java:254)
    at org.h2.message.DbException.throwInternalError(DbException.java:267)
    at org.h2.engine.Session.unlockAll(Session.java:985)
    at org.h2.engine.Session.endTransaction(Session.java:760)
    at org.h2.engine.Session.commit(Session.java:708)
    at org.h2.command.Command.stop(Command.java:157)
    at org.h2.command.CommandContainer.stop(CommandContainer.java:122)
    at org.h2.command.Command.executeUpdate(Command.java:296)
    at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:249)
    ... 23 more

org.h2.jdbc.JdbcSQLException: Concurrent update in table "SYS": another transaction has updated or deleted the same row [90131-197]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:357)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.table.RegularTable.removeRow(RegularTable.java:375)
    at org.h2.engine.Database.removeMeta(Database.java:985)
    at org.h2.engine.Database.updateMeta(Database.java:1698)
    at org.h2.command.ddl.Analyze.analyzeTable(Analyze.java:136)
    at org.h2.engine.Session.commit(Session.java:701)
    at org.h2.command.Command.stop(Command.java:157)
    at org.h2.command.CommandContainer.stop(CommandContainer.java:122)
    at org.h2.command.Command.executeUpdate(Command.java:296)
    at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:249)
    at com.verscend.dxcg.reports.db.h2.QueryHelper.insertOrUpdateTable(QueryHelper.java:20)
    at com.verscend.dxcg.audit.AuditLogGenerator.process(AuditLogGenerator.java:22)
    at org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:103)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:62)
    at org.apache.camel.processor.SendProcessor.process(SendProcessor.java:145)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:120)
    at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:198)
    at org.apache.camel.component.seda.SedaConsumer.sendToConsumers(SedaConsumer.java:298)
    at org.apache.camel.component.seda.SedaConsumer.doRun(SedaConsumer.java:207)
    at org.apache.camel.component.seda.SedaConsumer.run(SedaConsumer.java:154)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)

` 我在这里想念什么?

【问题讨论】:

我遇到了类似的问题。你找到解决办法了吗? 我在使用相同版本 (1.4.197) 时遇到了同样的问题,我使用的是单个数据库连接,我使用单个重复使用的 PreparedStatement 插入大量记录。插入使用标准的插入 SQL 语句。 【参考方案1】:

我找到了解决此问题的临时解决方法:

如果在插入记录时抛出异常(大约每 5-10.000 条记录发生一次,看似随机),线程等待 10 毫秒,然后重试插入记录。如果重试也失败,则向上传播异常,并且不插入记录。

到目前为止,等待和重试似乎给了数据库足够的时间来解锁有问题的记录。

【讨论】:

以上是关于表“SYS”中的并发更新:h2 数据库在插入多条记录时失败的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 2.x 实践记:H2 Database

Spring Boot 2.x 实践记:H2 Database

将MYSQL某一数据库中的多条记录批量插入到另一MYSQL数据库时,记录不存在则插入,存在则更新

在java中以多线程方式插入或更新数据库中的多条记录

mysql数据库!在线浏览表中内容,同时修改多条内容并更新至数据库

如果 mysql 数据库中的表中不存在,则更新或插入多条记录