为什么在MySQL中已经被另一个事务更新的行无法被更新。
为什么在MySQL中已经被另一个事务更新的行无法被更新。
我正在尝试理解MySQL InnoDB的可重复读隔离级别,但在尝试这两个事务时,它表现出我无法理解的行为。
以下是我对测试的初始化设置:
mysql> SHOW CREATE TABLE `test`;
+-------+---------------------------------------------------------------------------------------+
| 表名 | 创建表语句 |
+-------+---------------------------------------------------------------------------------------+
| test | CREATE TABLE `test` (
`ID` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
+-------+---------------------------------------------------------------------------------------+
1行记录(0.00秒)
mysql> SELECT * FROM `test`;
+----+
| ID |
+----+
| 1 |
+----+
1行记录(0.00秒)
mysql> SELECT @@TX_ISOLATION;
+-----------------+
| @@TX_ISOLATION |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1行记录, 1警告(0.00秒)
表格如下:
| 序号 | Tx1 | Tx2 |
|------|------------------------------|-------------------------------------------------------------|
| 1 | START TRANSACTION | START TRANSACTION |
| 2 | SELECT * FROM test; | SELECT * FROM test; |
| 3 | SELECT * FROM test WHERE ID = 1 FOR UPDATE; | |
| 4 | UPDATE test SET ID = 2 WHERE ID = 1; | UPDATE test SET ID = 3 WHERE ID = 1; # 需要等待锁 |
| 5 | SELECT * FROM test; | # 继续等待 |
| 6 | COMMIT; | |
| 7 | SELECT * FROM test; | SELECT * FROM test WHERE ID = 1; |
| 8 | | UPDATE test SET ID = 3 WHERE ID = 1; |
| 9 | SELECT * FROM test; | SELECT * FROM test; # 尽管行存在,但无法将1更新为3,仍返回2 |
| 10 | | UPDATE test SET ID = 3 WHERE ID = 2; |
| 11 | | COMMIT; |
| 12 | SELECT * FROM test; | SELECT * FROM test; |
我想知道MySQL在Tx2的第8行和第10行如何处理ID = 1和ID = 2。
如果我在Tx2的第4行使用"UPDATE test SET ID = 3 WHERE ID = 2",即使Tx1只持有ID = 1的独占锁,仍然需要等待锁吗?
为什么无法在MySQL中通过另一个事务已更新的行进行更新?
在第8步,没有锁定的记录,因为TX1
已经执行了COMMIT,这清除了锁定。
也没有ID=1的记录,因为它已经被更新了。
在第9和第10步,由于REPEATABLE READ,Tx2
无法更新记录。它无法“看到”id=2的记录,也找不到id=1的记录(因为它已经被删除/更改)。
只有在COMMIT之后,Tx2
才能看到新的/更改的数据。
但是ID=1仍然可以通过select在Tx2中看到。如果我们可以选择它但不能更新它,这是很奇怪的。更新一个不可见的ID也很奇怪。
由于REPEATABLE READ,它仍然可见,请参考这个很好的解释为什么这些数据不能被更改。(注意:读取的数据不能被更改,但数据库中的数据可以!)
以上是出现此问题的原因和解决方法的整理。