在同一线程中打开两个 JDBC 事务的死锁
Deadlock opening two JDBC transactions in the same thread
我正在通过 JDBC 测试一些数据库驱动程序。其中一项测试包括测试数据库的事务功能:
- 我打开两个到同一个数据库的连接(autoCommit=false)。
- 在连接 A 上,我在 table 中插入一行而不执行提交。
- 在连接 B 上,我预计还看不到该行。
- 在连接 A 上,我执行了提交。
- 在连接 B 上,我希望看到该行。
该测试适用于 Oracle 12,但对于其他数据库,如 HSQLDB、Derby 或 SQL Server,NO:它会阻塞第 3 步的中间。
我猜原因可能与事务隔离参数有关,但我在创建两个连接时尝试了所有可能的值,结果总是相同。
为什么会阻塞?为什么 Oracle 不阻塞?
这是我的代码:
public class JdbcTest
{
@Test
public void twoTransactionsTest()
throws SQLException,
IOException
{
String tableName="dummy01";
// 1. I open two connections to the same db (with autoCommit=false).
try (Connection connectionA=createConnection(); Connection connectionB=createConnection())
{
createTable(connectionA, tableName);
// 2. On connection A, I insert one row in a table without performing a commit.
execute(connectionA, "INSERT INTO " + tableName + " VALUES(50, 'grecia')");
// 3. On connection B, I expect not to see that row yet.
int records=queryAndCountRows(connectionB, "SELECT id FROM " + tableName + " WHERE id=50");
assertEquals(0, records);
// 4. On connection A, I perform a commit.
connectionA.commit();
// 5. On connection B, I expect to see that row.
records=queryAndCountRows(connectionB, "SELECT * FROM " + tableName + " WHERE id=50");
assertEquals(1, records);
dropTable(connectionA, tableName);
}
}
private Connection createConnection()
throws SQLException,
IOException
{
String url="jdbc:hsqldb:demo.hsqldb";
String user="demo";
String password="";
Connection connection=DriverManager.getConnection(url, user, password);
connection.setAutoCommit(false);
return connection;
}
private int queryAndCountRows(Connection connection, String sql)
throws SQLException
{
try (PreparedStatement pst=connection.prepareStatement(sql))
{
try (ResultSet rs=pst.executeQuery())
{
int records=0;
while (rs.next())
{
records++;
}
return records;
}
}
}
private void execute(Connection connection, String sql)
throws SQLException
{
try (Statement statement=connection.createStatement())
{
statement.execute(sql);
}
}
private void createTable(Connection connection, String tableName)
throws SQLException
{
try
{
execute(connection, "DROP TABLE " + tableName);
}
catch (SQLException e)
{
// If the table already exists, let's ignore this error.
}
execute(connection, "CREATE TABLE " + tableName + "(id NUMBER(5) NOT NULL, name VARCHAR2(100))");
}
private void dropTable(Connection connection, String tableName)
throws SQLException
{
execute(connection, "DROP TABLE " + tableName);
}
}
我的依赖项:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.1.0.jre8</version>
</dependency>
提前致谢。
在 Oracle 中,数据库读取器从不等待写入器,除非是在极度 基本上可以忽略的异常情况下。实际上,读者从不等待作者。
在其他数据库中,情况并非总是如此。例如在HSQLDB中,默认的锁模式是2PL(two-phase locking)。在该模型中,写入 table 会获取 table 上的独占锁,防止其他会话读取 table。它具有更复杂的锁定模型(例如 MVCC),可以使用 SET DATABASE TRANSACTION CONTROL
命令进行设置。
这是一个很好的例子,说明为什么 "database independence" 真的很难(而且为了我的钱,我从未设定为目标)。
如果你运行 HSQLDB 作为服务器并使用 MVCC,你将得到你期望的结果。
在进程内使用 HSQLDB 时,必须为每个连接使用单独的线程,并使用 MVCC 才能工作。
隔离在这些数据库系统中的实现方式不同,请按照此link了解更多信息:
http://www.dba-in-exile.com/2012/11/isolation-levels-in-oracle-vs-sql-server.html
简而言之,Oracle以writer不阻塞reader的方式实现,但其他RDBMS并非如此。
我正在通过 JDBC 测试一些数据库驱动程序。其中一项测试包括测试数据库的事务功能:
- 我打开两个到同一个数据库的连接(autoCommit=false)。
- 在连接 A 上,我在 table 中插入一行而不执行提交。
- 在连接 B 上,我预计还看不到该行。
- 在连接 A 上,我执行了提交。
- 在连接 B 上,我希望看到该行。
该测试适用于 Oracle 12,但对于其他数据库,如 HSQLDB、Derby 或 SQL Server,NO:它会阻塞第 3 步的中间。
我猜原因可能与事务隔离参数有关,但我在创建两个连接时尝试了所有可能的值,结果总是相同。
为什么会阻塞?为什么 Oracle 不阻塞?
这是我的代码:
public class JdbcTest
{
@Test
public void twoTransactionsTest()
throws SQLException,
IOException
{
String tableName="dummy01";
// 1. I open two connections to the same db (with autoCommit=false).
try (Connection connectionA=createConnection(); Connection connectionB=createConnection())
{
createTable(connectionA, tableName);
// 2. On connection A, I insert one row in a table without performing a commit.
execute(connectionA, "INSERT INTO " + tableName + " VALUES(50, 'grecia')");
// 3. On connection B, I expect not to see that row yet.
int records=queryAndCountRows(connectionB, "SELECT id FROM " + tableName + " WHERE id=50");
assertEquals(0, records);
// 4. On connection A, I perform a commit.
connectionA.commit();
// 5. On connection B, I expect to see that row.
records=queryAndCountRows(connectionB, "SELECT * FROM " + tableName + " WHERE id=50");
assertEquals(1, records);
dropTable(connectionA, tableName);
}
}
private Connection createConnection()
throws SQLException,
IOException
{
String url="jdbc:hsqldb:demo.hsqldb";
String user="demo";
String password="";
Connection connection=DriverManager.getConnection(url, user, password);
connection.setAutoCommit(false);
return connection;
}
private int queryAndCountRows(Connection connection, String sql)
throws SQLException
{
try (PreparedStatement pst=connection.prepareStatement(sql))
{
try (ResultSet rs=pst.executeQuery())
{
int records=0;
while (rs.next())
{
records++;
}
return records;
}
}
}
private void execute(Connection connection, String sql)
throws SQLException
{
try (Statement statement=connection.createStatement())
{
statement.execute(sql);
}
}
private void createTable(Connection connection, String tableName)
throws SQLException
{
try
{
execute(connection, "DROP TABLE " + tableName);
}
catch (SQLException e)
{
// If the table already exists, let's ignore this error.
}
execute(connection, "CREATE TABLE " + tableName + "(id NUMBER(5) NOT NULL, name VARCHAR2(100))");
}
private void dropTable(Connection connection, String tableName)
throws SQLException
{
execute(connection, "DROP TABLE " + tableName);
}
}
我的依赖项:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>6.1.0.jre8</version>
</dependency>
提前致谢。
在 Oracle 中,数据库读取器从不等待写入器,除非是在极度 基本上可以忽略的异常情况下。实际上,读者从不等待作者。
在其他数据库中,情况并非总是如此。例如在HSQLDB中,默认的锁模式是2PL(two-phase locking)。在该模型中,写入 table 会获取 table 上的独占锁,防止其他会话读取 table。它具有更复杂的锁定模型(例如 MVCC),可以使用 SET DATABASE TRANSACTION CONTROL
命令进行设置。
这是一个很好的例子,说明为什么 "database independence" 真的很难(而且为了我的钱,我从未设定为目标)。
如果你运行 HSQLDB 作为服务器并使用 MVCC,你将得到你期望的结果。
在进程内使用 HSQLDB 时,必须为每个连接使用单独的线程,并使用 MVCC 才能工作。
隔离在这些数据库系统中的实现方式不同,请按照此link了解更多信息:
http://www.dba-in-exile.com/2012/11/isolation-levels-in-oracle-vs-sql-server.html
简而言之,Oracle以writer不阻塞reader的方式实现,但其他RDBMS并非如此。