繁体   English   中英

如何让正在读取的行等待直到使用 JDBC 和 MySQL 释放锁定?

[英]How can I make a row being read wait until a lock on it is released using JDBC and MySQL?

所以我有这个小例子..

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import static java.sql.DriverManager.getConnection;

public class Bar {
    public static void main(String[] args) throws SQLException, InterruptedException {
        final long start = System.currentTimeMillis();
        final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
        final Statement statement = connection.createStatement();
        statement.executeUpdate("UPDATE actor SET first_name = 'bar' WHERE last_name = 'tugay'");
        statement.close();
        connection.close();
        final long end = System.currentTimeMillis();
        System.out.println("Took: " + (end - start)); // Will print around 350ms
    }
}

当我执行这小段代码时,它会打印出大约 350 ~ 400 毫秒的值,这很好。

现在,当我第一次启动以下代码时..

import java.sql.*;

import static java.sql.DriverManager.*;

public class Foo {

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

            final Statement statement = connection.createStatement();
            statement.executeUpdate("UPDATE actor SET first_name = 'foo' WHERE last_name = 'tugay'");

            System.out.println("Sleeping!");
            Thread.sleep(15000); // Sleep for 15 seconds..

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

然后运行 ​​Bar.java,我将获得大约 12 - 13 秒的值,这意味着当 Foo.java “锁定”lastname = 'tugay' 的行时,Bar.java 只是等待,然后将 first_name 设置为 'bar' .

如果 Bar.java 尝试读取 last_name = 'tugay' 的行,我想得到相同的行为。 所以这是我的代码:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import static java.sql.DriverManager.getConnection;

public class Bar {
    public static void main(String[] args) throws SQLException, InterruptedException {
        final long start = System.currentTimeMillis();
        final Connection connection = getConnection("jdbc:mysql://localhost:3306/sakila", "root", "root");
        final Statement statement = connection.createStatement();
        final ResultSet resultSet = statement.executeQuery("SELECT first_name FROM actor WHERE last_name = 'tugay'");
        resultSet.next();
        System.out.println(resultSet.getString("first_name"));
        statement.close();
        connection.close();
        final long end = System.currentTimeMillis();
        System.out.println("Took: " + (end - start));
    }
}

鉴于,最初数据库中的 first_name 值是“koray”,当我启动 Foo.java 并且它“休眠”时,当我运行 Bar.java 时,我会得到:

koray
Took: 390

有没有办法让 Bar.java 在读取时也等待,就像它在更新时等待一样?

您需要锁定读取

如果您不打算更新该行,而只想确保没有其他事务在该行上工作,请获取意向共享 ( IS ) 锁:

SELECT first_name FROM actor WHERE last_name = 'tugay' LOCK IN SHARE MODE

如果您打算在结果返回后更新该行,请获取 Intention eXclusive ( IX ) 锁:

SELECT first_name FROM actor WHERE last_name = 'tugay' FOR UPDATE

这两个查询都将阻塞,直到请求的锁可用。 由于ISIX锁与正在对行进行或已完成更新(尚未提交或回滚)的事务持有的排他X锁不兼容,因此上述任一查询都将阻塞,直到其他事务释放通过提交或回滚其X锁。

只有这样,该事务才能获得锁并接收其结果。

最后,这个事务最终通过提交或回滚释放它获得的锁。

另见https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-shared-exclusive-locks

更新:您可以使用明确启用锁定读取的Michael-sqlbot方法,或者您可以使用事务隔离级别 - TRANSACTION_SERIALIZABLE 并禁用自动提交,这将隐式启用锁定读取。

使用Connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE)

这里

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM