11

我在一个表中有 5 行(1 到 5)。我希望第 2 行锁定一些更新,同时如果有人试图更新第 4 行,那么他应该能够更新。

我正在用下面的代码尝试这个,但我觉得它把锁放在表级别而不是行级别。

------ 会话 1

START TRANSACTION;
SELECT * FROM test WHERE t=1 FOR UPDATE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;

----- 会话 2(被阻止)

START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
4

2 回答 2

10

而不是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 UNCOMMITTEDREAD COMMITTED事务级别。

最简单的是Option 1,恕我直言,但这取决于您的可能性。

于 2015-11-19T10:07:55.677 回答
0

我发现下面的选项更合适,我同时从并发会话中生成 40000 个数字。我没有找到任何重复的号码。如果没有以下命令,我会生成 10000 个数字并找到 5 个重复的数字。

开始交易

SELECT * FROM test WHERE t=1 FOR UPDATE;

更新测试 SET NAME='irfandd' WHERE t=2;

犯罪;

于 2015-11-20T10:33:38.447 回答