And*_*lov 6 java transactions jdbc h2 isolation-level
维基百科将Phantom读取现象描述为:
当在事务过程中执行两个相同的查询,并且第二个查询返回的行集合与第一个查询不同时,会发生幻像读取.
它还指出,对于可序列化的隔离级别,Phantom读取是不可能的.我试图确保它在H2中是这样,但要么我想错了,要么我做错了,或者H2出了问题.不过,这是代码:
try(Connection connection1 = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
connection1.setAutoCommit(false);
try(Connection connection2 = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
connection2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
connection2.setAutoCommit(false);
assertEquals(0, selectAll(connection1));
assertEquals(0, selectAll(connection2)); // A: select
insertOne(connection1); // B: insert
assertEquals(1, selectAll(connection1));
assertEquals(0, selectAll(connection2)); // A: select
connection1.commit(); // B: commit for insert
assertEquals(1, selectAll(connection1));
assertEquals(0, selectAll(connection2)); // A: select ???
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,我启动了2个并发连接,并将其中一个连接配置为具有可序列化的事务隔离.之后,我确保两者都看不到任何数据.然后,使用connection1
,我插入一个新行.在它之后,我确保这个新行是可见的connection1
,但不是connection2
.然后,我提交更改,并期望connection2
不断意识到这一变化.简而言之,我希望我的所有A: select
查询都返回相同的行集(在我的情况下为空集).
但这不会发生:最后一个selectAll(connection2)
返回刚刚插入并行连接的行.我错了,这种行为是预期的,还是H2出了问题?
以下是帮助方法:
public void setUpDatabase() throws SQLException {
try(Connection connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
try (PreparedStatement s = connection.prepareStatement("create table Notes(text varchar(256) not null)")) {
s.executeUpdate();
}
}
}
private static int selectAll(Connection connection) throws SQLException {
int count = 0;
try (PreparedStatement s = connection.prepareStatement("select * from Notes")) {
s.setQueryTimeout(1);
try (ResultSet resultSet = s.executeQuery()) {
while (resultSet.next()) {
++count;
}
}
}
return count;
}
private static void insertOne(Connection connection) throws SQLException {
try (PreparedStatement s = connection.prepareStatement("insert into Notes(text) values(?)")) {
s.setString(1, "hello");
s.setQueryTimeout(1);
s.executeUpdate();
}
}
Run Code Online (Sandbox Code Playgroud)
完整的测试在这里:https://gist.github.com/loki2302/26f3c052f7e73fd22604
我使用H2 1.4.185.
小智 3
在存在悲观锁定的情况下,启用隔离级别“可串行化”时,连接 1 和 2 上的前两次读取操作分别应导致两个共享(写入)锁。
随后insertOne(connection1)
需要的范围锁与外部事务 2 的共享锁不兼容。因此连接 1 将进入“等待”(轮询)状态。不使用setQueryTimeout(1)
您的应用程序就会挂起。
关于https://en.wikipedia.org/wiki/Isolation_(database_systems)#Phantom_reads,您应该更改您的应用程序(不使用setQueryTimeout
)以允许以下计划,方法是手动启动两个 JVM 实例或使用不同的线程:
Transaction 1 | Transaction 2 | Comment
--------------+---------------+--------
- | selectAll | Acquiring shared lock in T2
insert | - | Unable to acquire range lock
wait | - | T1 polling
wait | selectAll | T2 gets identical row set
wait | - |
wait | commit | T2 releasing shared lock
| | T1 resuming insert
commit | |
Run Code Online (Sandbox Code Playgroud)
如果不支持“可序列化”,您将看到:
Transaction 1 | Transaction 2 | Comment
--------------+---------------+--------
- | selectAll | Acquiring shared lock in T2
insert | - | No need for range lock due to missing support
commit | | T1 releasing all locks
| selectAll | T2 gets different row set
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1840 次 |
最近记录: |