为啥不能更新已经被 MySQL 中另一个事务更新的行
Posted
技术标签:
【中文标题】为啥不能更新已经被 MySQL 中另一个事务更新的行【英文标题】:Why can not update a row that already updated by another transaction in MySQL为什么不能更新已经被 MySQL 中另一个事务更新的行 【发布时间】:2020-12-27 16:37:32 【问题描述】:我正在尝试了解 mysql InnoDB 的可重复读取隔离级别。但是当我尝试这 2 个事务时,它的行为是我无法理解的。
这是我的测试初始化
mysql> SHOW CREATE TABLE `test`;
+-------+---------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+---------------------------------------------------------------------------------------+
| test | CREATE TABLE `test` (
`ID` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
+-------+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT * FROM `test`;
+----+
| ID |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
mysql> SELECT @@TX_ISOLATION;
+-----------------+
| @@TX_ISOLATION |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
No | Tx1 | Tx2 |
---|---|---|
1 | START TRANSACTION | START TRANSACTION |
2 | SELECT * FROM test ;# Return 1 |
SELECT * FROM test ;# Return 1 |
3 | SELECT * FROM test WHERE ID = 1 FOR UPDATE;# return 1 |
|
4 | UPDATE test SET ID = 2 WHERE ID = 1; # OK |
UPDATE test SET ID = 3 WHERE ID = 1; # Lock wait |
5 | SELECT * FROM test ;# Return 2 |
# Keep waiting |
6 | COMMIT; # OK | # Query OK: rows matched:0, changed: 0, warnings: 0 |
7 | SELECT * FROM test ;# Return 2 |
SELECT * FROM test WHERE ID = 1;# Return 1 |
8 | UPDATE test SET ID = 3 WHERE ID = 1; #Query OK: rows matched:0, changed: 0, warnings: 0 |
|
9 | SELECT * FROM test ;# Return 2 |
SELECT * FROM test ;# Return 1 <-- cannot update 1 to 3 even the row exists |
10 | UPDATE test SET ID = 3 WHERE ID = 2;#Query OK: rows matched:1, changed: 1, warnings: 0 |
|
11 | COMMIT; #OK | |
12 | SELECT * FROM test # Return 3 |
SELECT * FROM test # Return 3 |
我想知道 MySQL 如何在第 8 行和第 10 行处理 Tx2 中的 ID=1 和 ID=2。
如果我在 Tx2 的第 4 行使用UPDATE test SET ID = 3 WHERE ID = 2
,即使 Tx1 只持有 ID = 1 的排他锁,仍然需要锁定等待?
【问题讨论】:
ID不同时的锁等待可能是在等待间隙锁。 【参考方案1】:在第 8 步,不再有锁定记录,因为TX1
已执行 COMMIT,这将清除锁定。
也不再有 ID=1 的记录,因为它已被更新。
在步骤 9 和 10,Tx2
无法更新记录,因为可重复读取。它无法看到 id=2 的记录,也不再找到 id=1 的记录(因为它已经消失/更改了)。
只有在 COMMIT 之后,Tx2
才能看到新的/更改的数据。
【讨论】:
但是 ID=1 仍然可以通过选择在 Tx2 中看到。如果我们可以选择它但不能更新它,那就太奇怪了。更新一个不可见的 ID 也很奇怪。 由于可重复读取,它仍然可见,请参阅 this 很好的解释为什么无法更改此数据。 (注意:读取的数据不能更改,但您数据库中的数据可以!)以上是关于为啥不能更新已经被 MySQL 中另一个事务更新的行的主要内容,如果未能解决你的问题,请参考以下文章