【问题标题】:Read Commited Isolation level not working读取已提交隔离级别不起作用
【发布时间】:2020-08-18 01:21:01
【问题描述】:

我正在尝试了解 ACID 隔离原则。我已经编写了这部分代码:

ExecutorService executorService = Executors.newSingleThreadExecutor();

        Runnable t2 = () -> {
            System.out.println("Thread: " + Thread.currentThread().getName());
            Connection connection;

            try {
                connection = this.iDatabaseConnector.getConnection();

                PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM mssmbank.mssmbank.bankaccounts WHERE id = ?");
                preparedStatement.setInt(1, 490);

                ResultSet resultSet = preparedStatement.executeQuery();
                resultSet.next();

                System.out.println("Result: " + resultSet.getDouble("BALANCE"));

                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        };

        Runnable t1 = () -> {
            System.out.println("Thread: " + Thread.currentThread().getName());
            Connection connection;

            try {
                connection = this.iDatabaseConnector.getConnection();
                connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                connection.setAutoCommit(false);

                PreparedStatement preparedStatement = connection.prepareStatement("UPDATE mssmbank.mssmbank.bankaccounts " + "SET balance = ? WHERE id = ?");
                preparedStatement.setDouble(1, (new Random()).nextInt(1000));
                preparedStatement.setInt(2, 490);
                preparedStatement.executeUpdate();

                ExecutorService executorService1 = Executors.newSingleThreadExecutor();
                executorService1.execute(t2);

                Thread.sleep(3_000);

                connection.commit();
                connection.close();

                executorService1.shutdown();
                executorService1.awaitTermination(20, TimeUnit.SECONDS);
            } catch (SQLException | InterruptedException e) {
                e.printStackTrace();
            }
        };

        executorService.execute(t1);
        executorService.shutdown();
        executorService.awaitTermination(20, TimeUnit.SECONDS);

有 2 个线程,t1t2t1 应该更新数据库中的银行帐户行,t2 应该读取其余额。

以下是场景:t1 启动、打开连接、进行更新并且不提交。它会触发 t2sleeps 3 秒。

t2 启动,打开连接,读取余额并打印(基本上是旧的,因为 t1 尚未提交)然后关闭连接。

然后t1通过提交更新结束,程序结束。

我期望发生的是,t2 被阻止,直到 t1 完成提交,因为 t2 正在访问同一个银行帐户行(t1 应该已锁定该行以进行更新)。因此,t2 应该打印了 #490 银行账户的更新余额,而不是之前的余额。

为什么t1 不锁定行?为什么 t2 没有被阻止? 请注意,隔离级别设置为TRANSACTION_READ_COMMITTED

【问题讨论】:

  • 这不是事务读取提交的工作方式。 t2 获取先前提交的值,即先前的值。这就是为什么您通常会快速更新和提交,以最小化另一个事务可以读取旧的、先前提交的值的窗口。
  • 谢谢,这很清楚。所以这不是关于脏读的场景吗?

标签: java multithreading postgresql jdbc isolation-level


【解决方案1】:

t2 中的 select 语句没有被阻塞,因为它只是尝试读取而不是写入。如果您要尝试执行更新(或选择...进行更新),那么如果您的超时设置为在放弃之前等待一段时间,它可能会阻塞。你没有提到你正在使用哪个数据库,但这里有一些关于 Postgres 如何处理锁定的好文档: https://www.postgresql.org/docs/current/explicit-locking.html

【讨论】:

  • 谢谢你明确的回答。是的,我正在使用 PostgreSQL。但是告诉我,这种情况不是脏读的例子吗?还是脏读仅与回滚发生时有关?
  • 我的荣幸。关于脏读,这在 PostgreSQL 中是不可能的。详情见postgresql.org/docs/current/transaction-iso.html
  • 哦,对了,我可以从那里的表格中看到,脏读在 PostgreSQL 中永远不会发生。非常感谢!
  • 我将 DatabaseConnector 更改为 MySQL 连接器,并尝试了相同的过程(相同的代码),它的脏读权限也很好。因此为 PostgreSQL 设置了 UNCOMMITED 或 COMMITED 隔离级别,它将始终不允许脏读。听起来比 MySQL 更安全!
猜你喜欢
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多