[英]Spring Data: How to lock a row in a transaction and make other transactions wait until it is released?
[英]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
这两个查询都将阻塞,直到请求的锁可用。 由于IS
和IX
锁与正在对行进行或已完成更新(尚未提交或回滚)的事务持有的排他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.