【问题标题】:Does MariaDB `FOR UPDATE` clause works fine?MariaDB `FOR UPDATE` 子句可以正常工作吗?
【发布时间】:2022-01-14 16:18:22
【问题描述】:

我正在尝试摆脱写入偏差并尝试使用可序列化的隔离级别,但我却陷入了死锁。我发现可序列化的隔离级别会因为this而导致死锁:

这个级别类似于 REPEATABLE READ,但 InnoDB 将所有普通的 SELECT 语句隐式转换为 SELECT ... LOCK IN SHARE MODE

所以,我尝试像这样使用REPEATABLE READ(没有id为“some_id”的行):

-- connection 1:
 START TRANSACTION;
 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
 select * from some_table where id="some_id" for update;
 
-- connection 2:
 START TRANSACTION;
 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
 select * from some_table where id="some_id" for update;
 insert into some_table values("some_id");

因此,我在连接 2 中收到此消息:Lock wait timeout exceeded; try restarting transaction

MariaDB for update docs says:

SELECT 的 FOR UPDATE 子句仅在自动提交设置为 0 或 SELECT 包含在事务中时适用。获取行上的锁,并且阻止其他事务写入行、获取锁和读取它们(除非它们的隔离级别是 READ UNCOMMITTED)。

但看起来它并没有阻止其他事务获取锁或读取行。

我做错了什么?

【问题讨论】:

    标签: transactions mariadb


    【解决方案1】:

    这篇文章回答了我的问题:How do I lock on an InnoDB row that doesn't exist yet?

    虽然上面的答案是正确的,因为 SELECT ... FOR UPDATE 将 防止并发会话/事务插入相同的 记录,这不是全部真相。我目前正在与 同样的问题,并得出的结论是 SELECT ... FOR UPDATE 在这种情况下几乎没有用,原因如下:

    并发事务/会话也可以执行 SELECT ... FOR UPDATE 在相同的记录/索引值上,MySQL 会很乐意接受 立即(非阻塞)并且不会引发错误。的 当然,一旦其他会话完成了该操作,您的会话将作为 好吧,不能再插入记录了。也不是你的,也不是其他的会话 /transaction 获取有关情况的任何信息并认为他们 可以安全地插入记录,直到他们真正尝试这样做。试 然后插入会导致死锁或重复键错误, 视情况而定。

    换句话说,SELECT ... FOR UPDATE 阻止其他会话 插入相应的记录,但即使您执行 SELECT ... FOR UPDATE 并且没有找到相应的记录,很可能是 您实际上无法插入该记录。恕我直言,这使得“第一个 查询,然后插入”方法没用。

    问题的原因是 MySQL 没有提供任何方法 真正锁定不存在的记录。两个并发会话 / 事务可以同时锁定不存在的记录“FOR UPDATE” 时间,一件真正不应该发生的事情,它使 开发难度大大增加。

    解决此问题的唯一方法似乎是使用信号量表或 插入时锁定整个表。请参考 MySQL 有关锁定整个表或使用的进一步参考的文档 信号量表。

    只要我的 2 美分 ...

    我没有先找到问题,所以我不会删除这个问题(我知道,实际上它是重复的),以便其他人更容易搜索。

    因此,我创建了唯一索引,并将它与可重复的读取隔离级别一起使用(没有“FOR UPDATE”)。它允许我检测并发插入并在我的代码中处理这种情况(我决定在这种情况下返回错误)。

    我在尝试回答这个问题时发现的相关资源:

    https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlock-example.html

    https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html

    https://mariadb.com/kb/en/set-transaction/

    https://mariadb.com/kb/en/lock-in-share-mode/

    https://mariadb.com/kb/en/for-update/

    https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

    https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlocks.html

    Deadlock in transaction with isolation level serializable

    NHibernate lock database table to avoid insert "duplicates"

    How to avoid MySQL 'Deadlock found when trying to get lock; try restarting transaction'

    【讨论】:

      猜你喜欢
      • 2016-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-17
      • 2022-07-06
      • 2016-08-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多