【问题标题】:How can I see TRANSACTION_REPEATABLE_READ in action with JDBC / MySQL?如何查看 TRANSACTION_REPEATABLE_READ 与 JDBC / MySQL 的关系?
【发布时间】:2017-11-13 05:40:01
【问题描述】:

我目前在我的计算机上运行 MySQL 服务器 (5.7.16),并且在此服务器上名为“Sakila”的数据库中有此示例行:

mysql> SELECT * FROM actor WHERE last_name = 'tugay';
+----------+------------+-----------+---------------------+
| actor_id | first_name | last_name | last_update         |
+----------+------------+-----------+---------------------+
|      201 | koray      | tugay     | 2017-06-11 21:42:08 |
+----------+------------+-----------+---------------------+

这是 JDBC 为我的 MySQL 服务器返回的 TRANSACTION ISOLATION LEVEL:

public static void main(String[] args) throws SQLException, InterruptedException {
    final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
    System.out.println(connection.getTransactionIsolation()); // Prints 4!
    connection.close();
}

这里的4TRANSACTION_REPEATABLE_READ 的常量documented,如下所示:

int TRANSACTION_REPEATABLE_READ  = 4;
A constant indicating that dirty reads are prevented. 
This level prohibits a transaction from reading a row with uncommitted changes in it.

我想看看这种行为,所以我有 2 个主要方法,第一个是:

import java.sql.*;

import static java.sql.DriverManager.*;

public class Foo {

    public static void main(String[] args) throws SQLException, InterruptedException {
        final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
        connection.setAutoCommit(false);

        final Statement statement = connection.createStatement();
        statement.executeUpdate("UPDATE actor SET first_name = 'ALTERED' WHERE first_name = 'koray'");

        System.out.println("Sleeping!");

        // Here I am expecting MySQL to lock the Row so no other Connection
        // can read data from it. This row will be updated, so unless the transaction is 
        // either commited or rolled back, the read data will be 'dirty'.
        Thread.sleep(5000);

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

如您所见,事务处于自动提交模式,我将行锁定 5 秒。当这个进程处于休眠状态时,我也会不断地运行以下代码:

public class Bar {

    public static void main(String[] args) throws SQLException, InterruptedException {
        final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
        connection.setAutoCommit(false);

        final Statement statement = connection.createStatement();
        final ResultSet resultSet = statement.executeQuery("SELECT * FROM actor WHERE last_name = 'tugay'");

        resultSet.next();
        System.out.println(resultSet.getString("first_name"));

        connection.close();
    }
}

但即使我在控制台中看到“睡眠”文本,只要我在 Bar 中运行 main 方法,我也会看到类似的值

koray
koray
koray
ALTERED

我的期望是永远不会看到 koray,因为这是一个“脏数据”,第一个 java 进程应该锁定该行。

我缺少什么以及如何锁定这一行?

【问题讨论】:

    标签: java mysql sql jdbc locking


    【解决方案1】:

    你应该再读一遍定义:

    此级别禁止事务读取未提交的行 变化。

    ALTERED 是脏数据,因为它没有被提交。科雷是正确的。酒吧禁止看到“ALTERED”。只要交易未提交,您就会一直看到“koray”。

    【讨论】:

    • 我认为这不能回答我的问题。
    • @KorayTugay 这个正是回答了你的问题。隔离级别通过向您显示更新之前的值来防止您看到未提交的值。它不会阻止您看到旧值或让您等待。行锁(X 锁)确实存在,但要让另一个事务阻止它,第二个事务必须尝试获取不兼容的锁。如果你想要你描述的行为,第二个事务必须SELECT ... LOCK IN SHARE MODE(用于IS 锁定)或SELECT ... FOR UPDATE(用于IX 锁定)。
    • @KorayTugay 你能否再澄清一下,什么让你感到困惑?
    • 我想我现在明白了,经过 Micael 的解释。我的期望是错误的。我的理解是“koray”是肮脏的数据,似乎“ALTERED”是肮脏的..
    • 你能看看:stackoverflow.com/questions/44501681/… 吗? @Michael-sqlbot
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2016-03-28
    相关资源
    最近更新 更多