java - SELECT FOR UPDATE 在迁移期间锁定数据库

标签 java mysql sql jdbc transactions

我试图在数据库迁移期间阻止并发修改,我的方法是使用 SELECT ... FOR UPDATE 锁定现有表 t (prereq) 并实现锁定期间迁移。

因此我创建了一个简单的示例,如下所示:

Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/testdb", "root", "");

conn.setAutoCommit(false);
statement = conn.createStatement();
ResultSet rs = statement.executeQuery("SELECT id FROM t FOR UPDATE");
rs.next();
System.out.println(rs.getInt(1));

System.out.println("Sleeping...");
Thread.sleep(10000);

statement.executeUpdate("CREATE TABLE t2 (id int PRIMARY KEY)");
// Release lock
statement.executeUpdate("UPDATE t SET id = 2 WHERE id = 1");

conn.commit();

statement.close();
conn.close();

其中 t 是一个只有 id int PRIMARY KEY 的虚拟表。

我设置了一个 Thread.sleep(10000); 来模拟长进程。在 Thread.sleep(10000); 期间,我正在运行第二个(类似的代码)来模拟并发

Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/testdb", "root", "");

Statement statement = conn.createStatement();
conn.setAutoCommit(false);
statement = conn.createStatement();
ResultSet rs = statement.executeQuery("SELECT id FROM t FOR UPDATE");
rs.next();
System.out.println(rs.getInt(1));

conn.commit();

statement.close();
conn.close(); 

但我希望代码打印 2.. 它打印了 1

当我检查 MySQL 通用日志时,我看到了那个东西

438 Query   SET autocommit=0
438 Query   SELECT id FROM t FOR UPDATE
439 Query   /* mysql-connector-java-5.1.35 ( Revision: 5fb9c5849535c13917c2cf9baaece6ef9693ef27 ) */SHOW VARIABLES WHERE Variable_name ='language' OR Variable_name = 'net_write_timeout' OR Variable_name = 'interactive_timeout' OR Variable_name = 'wait_timeout' OR Variable_name = 'character_set_client' OR Variable_name = 'character_set_connection' OR Variable_name = 'character_set' OR Variable_name = 'character_set_server' OR Variable_name = 'tx_isolation' OR Variable_name = 'transaction_isolation' OR Variable_name = 'character_set_results' OR Variable_name = 'timezone' OR Variable_name = 'time_zone' OR Variable_name = 'system_time_zone' OR Variable_name = 'lower_case_table_names' OR Variable_name = 'max_allowed_packet' OR Variable_name = 'net_buffer_length' OR Variable_name = 'sql_mode' OR Variable_name = 'query_cache_type' OR Variable_name = 'query_cache_size' OR Variable_name = 'license' OR Variable_name = 'init_connect'
439 Query   /* mysql-connector-java-5.1.35 ( Revision: 5fb9c5849535c13917c2cf9baaece6ef9693ef27 ) */SELECT @@session.auto_increment_increment
439 Query   SET character_set_results = NULL
439 Query   SET autocommit=1
439 Query   SET sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'
439 Query   SET autocommit=0
439 Query   SELECT id FROM t FOR UPDATE
438 Query   CREATE TABLE t2 (id int PRIMARY KEY)
439 Query   commit
439 Query   rollback
439 Quit    
438 Query   UPDATE t SET id = 2 WHERE id = 1
438 Query   commit
438 Query   rollback

CREATE TABLE t2... 事务( session 2)提交后释放锁,但我在 autoCommit(false) 为什么 MySQL 强制 在这种情况下提交

我可能还误解了 SELECT ... FOR UPDATE 的工作原理...


更新:在没有 Java 和 JDBC 的情况下使用 mysql 客户端进行了测试

SESSION1> CREATE TABLE t (id int PRIMARY KEY) ENGINE=INNODB;
SESSION1> INSERT INTO t VALUES (1);
SESSION1> BEGIN;
SESSION1> SELECT id FROM t FOR UPDATE;

SESSION2> BEGIN;
SESSION2> SELECT id FROM t FOR UPDATE;
--> HERE SESSION2 is stuck (expected behavior)

SESSION1> CREATE TABLE t2 (id int PRIMARY KEY) ENGINE=INNODB;
--> HERE SESSION2 is unlock returning 
+----+
| id |
+----+
|  1 |
+----+
1 row in set (4.15 sec)

使用 DLL 语句的 SELECT ... FOR UPDATE 似乎没有像我预期的那样工作

最佳答案

如果您的存储引擎是 MyISAM,您将观察到这种行为。 MyISAM 不支持事务,因此,您的第一个代码不会放置任何锁,因为所有语句都是自动提交的,因此您的第二个代码将愉快地游过他们自己的 SELECT FOR UPDATE 并直接进行 SELECT ID。

长话短说:将数据库切换到 InnoDB 存储以使其正常工作。

引用资料:

https://dev.mysql.com/doc/refman/5.0/en/myisam-storage-engine.html

https://dev.mysql.com/doc/refman/5.5/en/innodb-storage-engine.html

关于java - SELECT FOR UPDATE 在迁移期间锁定数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31819282/

相关文章:

java - 牛顿法计算速率

Java Flight Recorder - Live Objects 选项卡为空

MySQL 错误 #1071 - 指定的 key 太长;最大 key 长度为 767 字节

php - Laravel - 使用数据库字符串创建动态 URL

sql - 如何在sql developer的 "Enter Binds"对话框中使用日期变量?

java - Long 到 XMLGregorianCalendar 并返回到 Long

java - 从 .der 更新/生成新的 .p12/.jks

php - 获取“org.apache.http.conn.httphostconnectexception 连接到 http ://x. x.x.x :88/mobile. php 被拒绝

mysql - 如何使用 byte[] 在 Entity Framework Core 的 MySQL 中存储 'blob' 类型?

用于存储构建版本的 SQL 数据类型