在嵌入模式下使用 h2,我正在从先前由 h2 使用 SCRIPT 命令生成的脚本备份恢复内存数据库。
我使用这个网址:
jdbc:h2:mem:main
我这样做:
FileReader script = new FileReader("db.sql");
RunScript.execute(conn,script);
根据文档,它应该类似于此 SQL:
RUNSCRIPT FROM 'db.sql'
而且,在我的应用程序中,它们确实执行相同的操作。但是,如果我使用 h2.bat 使用 Web 控制台运行负载,则会得到不同的结果。
在我的应用程序中加载此数据后,我知道有些行已加载,但我无法通过查询访问。这些查询证明了这一点:
select count(*) from MY_TABLE yields 96576
select count(*) from MY_TABLE where ID <> 3238396 yields 96575
select count(*) from MY_TABLE where ID = 3238396 yields 0
加载 Web 控制台并使用相同的 RUNSCRIPT 命令和文件加载会生成一个数据库,我可以在其中找到具有该 ID 的行。
我的第一 react 是我正在处理某种锁定问题。我尝试了以下方法(结果没有变化):
- 在 RunScript.execute() 之后手动发出 conn.commit()
- 将 ;LOCK_MODE=3 和 ;LOCK_MODE=0 附加到我的网址
有任何关于如何识别正在发生的事情的正确方向的指针吗?我最终插入:
Server.createWebServer("-trace","-webPort","9083").start()
这样我就可以通过 Web 控制台运行这些查询来检查通过 JDBC 返回的内容。该问题在我的应用程序中始终发生,并且始终不会通过网络控制台发生。所以一定有什么东西在起作用。
表模式并不奇特。这是来自
的架构列select * from INFORMATION_SCHEMA.TABLES where TABLE_NAME='MY_TABLE'
CREATE MEMORY TABLE PUBLIC.MY_TABLE(
ID INTEGER SELECTIVITY 100,
P_ID INTEGER SELECTIVITY 4,
TYPE VARCHAR(10) SELECTIVITY 1,
P_ORDER DECIMAL(8, 0) SELECTIVITY 11,
E_GROUP INTEGER SELECTIVITY 1,
P_USAGE VARCHAR(16) SELECTIVITY 1
)
任何朝正确方向的插入都将受到非常赞赏。
编辑
因此,在运行 RunScript 命令加载数据库后,数据库似乎以某种方式损坏了。当我试图调试以找出发生了什么时,我尝试执行以下命令:
delete from MY_TABLE where ID <> 3238396
我最终得到:
Row not found when trying to delete from index "PUBLIC.MY_TABLE_IX1: 95326", SQL statement:
delete from MY_TABLE where ID <> 3238396 [90112-178] 90112/90112 (Help)
然后,我尝试从上下文中删除并重新创建所有索引,但这对整体问题没有影响。
救命!
编辑2
更多信息:该问题是由于创建索引而发生的。 (我相信我在 h2 中发现了一个错误,并且我正在努力创建一个重现它的最小案例)。如果您有正确的数据集,下面的简单代码将重现该问题。
public static void main(String[] args)
{
try
{
final String DB_H2URL = "jdbc:h2:mem:main;LOCK_MODE=3";
Class.forName("org.h2.Driver");
Connection c = DriverManager.getConnection(DB_H2URL, "sa", "");
FileReader script = new FileReader("db.sql");
RunScript.execute(c,script);
script.close();
Statement st = c.createStatement();
ResultSet rs = st.executeQuery("select count(*) from MY_TABLE where P_ID = 3238396");
rs.next();
if(rs.getLong(1) == 0)
System.err.println("It happened");
else
System.err.println("It didn't happen");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
我已将 db.sql 脚本减少到大约 5000 行,但这种情况仍然发生。当我尝试达到 2500 行时,它停止了。如果我删除 db.sql 的最后一行(这是索引创建),问题也将停止发生。最后一行是这样的:
CREATE INDEX PUBLIC.MY_TABLE_IX1 ON PUBLIC.MY_TABLE(P_ID);
但数据在这方面发挥着重要作用。它似乎仍然只是一行,并且索引以某种方式使其无法访问。
编辑3
我已经确定了要重现的最小数据示例。我将表模式剥离为单个列,我发现该列中的值似乎并不重要——只是行数。这是通过 SCRIPT 命令生成的 db.sql 的内容(用明显的内容进行了剪裁):
;
CREATE USER IF NOT EXISTS SA SALT '8eed806dbbd1ea59' HASH '6d55cf715c56f4ca392aca7389da216a97ae8c9785de5d071b49de5436b0c003' ADMIN;
CREATE MEMORY TABLE PUBLIC.MY_TABLE(
P_ID INTEGER SELECTIVITY 100
);
-- 5132 +/- SELECT COUNT(*) FROM PUBLIC.MY_TABLE;
INSERT INTO PUBLIC.MY_TABLE(P_ID) VALUES
(1),
(2),
(3),
... snipped you obviously have breaks in the bulk insert here ...
(5143),
(3238396);
CREATE INDEX PUBLIC.MY_TABLE_IX1 ON PUBLIC.MY_TABLE(P_ID);
但这会重现问题。 [请注意,每次有批量插入行时,我的编号都会跳过一个数字。所以确实有 5132 行,尽管您看到 5143 select count(*) from MY_TABLE 产生 5132]。另外,我现在似乎可以通过执行以下操作直接在 WebConsole 中重新创建问题:
drop table MY_TABLE
runscript from 'db.sql'
select count(*) from MY_TABLE where P_ID = 3238396
如果当您知道其中有一行时从选择中返回 0,则您已经重新创建了问题。
奇怪的是,我似乎能够做到
select * from MY_TABLE order by P_ID desc
此时我可以看到该行。但直接进入行:
select * from MY_TABLE where P_ID = 3238396
没有任何结果。
我刚刚意识到我应该注意我正在使用h2-1.4.178.jar
最佳答案
h2 人员显然已经解决了这个问题。
https://code.google.com/p/h2database/issues/detail?id=566
只需从版本控制获取代码或等待下一个版本构建。谢谢托马斯。
关于h2 索引损坏?加载了 runscript 的嵌入式数据库有 "invisible"行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24267914/