繁体   English   中英

选择更新以在迁移期间锁定数据库

[英]SELECT FOR UPDATE to lock database during migration

我试图阻止数据库迁移期间的并发修改,我的方法是使用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...之后, commit了释放锁的事务(会话2),但是我处于autoCommit(false)为什么MySQL在这种情况下强制commit

我也可能会误解SELECT ... FOR UPDATE工作原理...


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

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

最后,在互斥锁定( SELECT ... FOR UPDATE )期间无法创建CREATE TABLE类的DLL语句。

但是,为了满足我的需要,我将使用与flyway相同的策略。 确实,flyway使用了两个会话:

  1. 要锁定的元表会话(元表是飞行路线的核心表)
  2. 对象会话以执行迁移

因此具有不同会话锁定机制的作品,可以使用mysql cli进行模拟

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

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

SESSION-OBJECT> BEGIN;
SESSION-OBJECT> CREATE TABLE t2 (id int PRIMARY KEY);
--> HERE SESSION-CONCURRENT is not unlock
SESSION-OBJECT> COMMIT;
--> SESSION-CONCURRENT still stuck even after COMMIT;

SESSION-METATABLE>UPDATE t SET id=2 WHERE id=1;
SESSION-METATABLE>COMMIT;
--> HERE SESSION-CONCURRENT is unlock and print
+----+
| id |
+----+
|  2 |
+----+
1 row in set (23.33 sec)

暂无
暂无

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

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