【问题标题】:Deadlock opening two JDBC transactions in the same thread死锁在同一个线程中打开两个 JDBC 事务
【发布时间】:2017-10-27 13:26:05
【问题描述】:

我正在通过 JDBC 测试一些数据库驱动程序。其中一项测试是测试数据库的事务能力:

  1. 我打开了两个到同一个数据库的连接(autoCommit=false)。
  2. 在连接 A 上,我在表中插入一行而不执行提交。
  3. 在连接 B 上,我预计不会看到该行。
  4. 在连接 A 上,我执行了提交。
  5. 在连接 B 上,我希望看到该行。

该测试适用于 Oracle 12,但对于 HSQLDB、Derby 或 SQL Server 等其他数据库,:它在第 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>

提前致谢。

【问题讨论】:

标签: java sql-server oracle jdbc hsqldb


【解决方案1】:

在 Oracle 中,数据库读取器从不等待写入器,除了可能非常您基本上可以忽略的不寻常情况。出于所有实际目的,读者从不等待作者。

在其他数据库中,情况并非总是如此。例如,在 HSQLDB 中,默认的锁定模式是 2PL(两阶段锁定)。在该模型中,写入表会获取该表的排他锁,从而阻止其他会话读取该表。它具有更复杂的锁定模型(例如 MVCC),可以使用 SET DATABASE TRANSACTION CONTROL 命令进行设置。

这是一个很好的例子,说明为什么“数据库独立性”真的很难(而且为了我的钱,我从来没有设定为目标)。

【讨论】:

    【解决方案2】:

    这些数据库系统中的隔离实现方式不同,请点击此链接了解更多信息:

    http://www.dba-in-exile.com/2012/11/isolation-levels-in-oracle-vs-sql-server.html

    简而言之,Oracle 以一种写入器不会阻塞读取器的方式实现它,但对于其他 RDBMS 而言并非如此。

    【讨论】:

      【解决方案3】:

      如果您将 HSQLDB 作为服务器运行并使用 MVCC,您将获得预期的结果。

      当您在进程内使用 HSQLDB 时,您必须为每个连接使用单独的线程,并使用 MVCC 使其工作。

      【讨论】:

      • 不幸的是,作为服务器+MVCC,我都无法避免死锁。无论如何,谢谢你的提示。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-11
      • 1970-01-01
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多