而不是FOR UPDATE
使用LOCK IN SHARE MODE
. FOR UPDATE
防止其他事务也读取该行。LOCK IN SHARE MODE
允许读取,但阻止更新。
参考:MySQL 手册
------ 会话 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 LOCK IN SHARE MODE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
-----会话2(不再被阻止:))
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
更新:
意识到该表在上没有索引t
,我有以下解释:
首先,事务 T1 将第 1 行锁定在SELECT * FROM test WHERE t=1 FOR UPDATE
接下来,事务 T2 尝试执行UPDATE test SET NAME='irfandd' WHERE t=4
。要找出受影响的行,它需要扫描所有行,包括第 1 行。但这已被锁定,因此 T2 必须等到 T1 完成。如果有任何类型的索引,WHERE t=4
可以使用索引来决定第 1 行是否包含t=4
,因此无需等待。
选项 1:添加索引,test.t
以便您的更新可以使用它。
选项 2: use LOCK IN SHARE MODE
,仅用于放置读锁。不幸的是,这个选项会造成死锁。有趣的是,T2 事务执行(更新第 4 行),而 T1 失败(更新第 2 行)。似乎 T1也对第 4 行进行了读锁定,并且由于 T2 对其进行了修改,因此 T1 由于事务隔离级别(默认情况下为 REPEATABLE READ)而失败。最终的解决方案是使用事务隔离级别,使用READ UNCOMMITTED
或READ COMMITTED
事务级别。
最简单的是Option 1,恕我直言,但这取决于您的可能性。