【问题标题】:Row Level Locking in MysqlMysql中的行级锁定
【发布时间】:2015-11-19 09:45:49
【问题描述】:

我在一个表中有 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;

【问题讨论】:

  • 你的存储引擎是什么?
  • 我正在使用 innodb + 使用没有主键约束

标签: mysql innodb rowlocking


【解决方案1】:

使用LOCK IN SHARE MODE 代替FOR UPDATEFOR UPDATE 也阻止其他事务读取该行。 LOCK IN SHARE MODE 允许读取,但阻止更新。

参考:MySQL Manual

-------- 会话 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锁定SELECT * FROM test WHERE t=1 FOR UPDATE中的第1行

接下来,事务 T2 尝试执行UPDATE test SET NAME='irfandd' WHERE t=4。要找出受影响的行,它需要扫描所有行,包括第 1 行。但这已被锁定,因此 T2 必须等到 T1 完成。 如果有任何类型的索引,WHERE t=4 可以使用索引来判断 row 1 是否包含t=4,因此无需等待。

选项 1:test.t 上添加索引,以便您的更新可以使用它。

选项 2: 使用 LOCK IN SHARE MODE,仅用于放置读锁。 不幸的是,这个选项会造成死锁。有趣的是,T2 事务执行(更新第 4 行),而 T1 失败(更新第 2 行)。似乎 T1 也读取了 第 4 行,并且由于 T2 对其进行了修改,因此 T1 由于事务隔离级别 (REPEATABLE READ by default) 而失败。最终的解决方案是使用Transaction Isolation Levels,使用READ UNCOMMITTEDREAD COMMITTED 事务级别。

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

【讨论】:

  • 不符合我的要求。我想要特定的行锁,也不想要整个表的锁。
  • 似乎如果您在t 上有任何类型的索引(主索引、唯一索引或通用索引),则表上没有锁,使用FOR UPDATELOCK IN SHARE MODE。如果您没有索引,则会出现锁定。它与索引有一定的关系。用更多细节更新答案。
【解决方案2】:

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

开始交易

SELECT * FROM test WHERE t=1 FOR UPDATE;

UPDATE test SET NAME='irfandd' WHERE t=2;

提交;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-11
    • 2016-05-03
    • 2013-09-23
    • 1970-01-01
    相关资源
    最近更新 更多