当前位置: 首页 > 知识库问答 >
问题:

MariaDB的“FOR UPDATE”子句是否有效?

杨飞飙
2023-03-14

我试图摆脱写歪斜,并尝试使用可序列化的隔离级别,但我反而遇到了死锁。我发现可序列化隔离级别可能会导致死锁,原因如下:

这个级别类似于 REPEATABLE READ,但 InnoDB 隐式将所有纯 SELECT 语句转换为 SELECT ...锁定共享模式

所以,我试着像这样使用< code>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中收到了以下消息:<code>超过了锁定等待超时;尝试重新启动事务。

MariaDB更新文档称:

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

但看起来它不会阻止其他事务获取锁或读取行。

我做错了什么?

共有1个答案

田翰林
2023-03-14

这篇文章回答了我的问题:如何锁定尚不存在的InnoDB行?

虽然上面的答案是正确的,因为SELECT… FORUPDATE将阻止并发会话/事务插入相同的记录,但这并不是全部事实。我目前正在与同样的问题作斗争,并得出结论,SELECT… FORUPDATE在这种情况下几乎毫无用处,原因如下:

并发事务/会话也可以进行选择...对于完全相同的记录/索引值的更新,MySQL会很乐意立即接受(非阻塞的)并且不会抛出错误。当然,一旦另一个会话这样做了,您的会话也不能再插入记录。无论是您的还是其他会话/事务都没有得到任何有关这种情况的信息,并认为他们可以安全地插入记录,直到他们实际尝试这样做。根据具体情况,尝试插入会导致死锁或重复键错误。

换句话说,选择...FOR UPDATE 可防止其他会话插入相应的记录,但即使您执行 SELECT ...对于 UPDATE 并且找不到相应的记录,则很可能您实际上无法插入该记录。恕我直言,这使得“先查询,然后插入”方法无用。

问题的原因是MySQL没有提供任何方法来真正锁定不存在的记录。两个并发会话/事务可以同时锁定不存在的记录“FOR UPDATE”,这确实是不可能的,并且使开发变得更加困难。

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

只是我的两分钱...

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

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

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

https://dev . MySQL . com/doc/ref man/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/ref man/8.0/en/innodb-deadlocks . html

具有可序列化隔离级别的事务中的死锁

锁定数据库表以避免插入“重复项”

如何避免MySQL在试图获取锁时发现死锁;'尝试重新启动交易'

 类似资料:
  • MariaDB 子句用于从表中获取数据。它也被用来连接表,这将在后面章节中学习。 语法: 示例1: 假设要从表中检索所有学生信息。参考以下语句中,语句的用法 - 执行上面查询语句,得到以下结果 - 示例2: 从students表中选择一个特定的列。例如,要选择和列,如下查询语句 - 执行上面查询语句,得到以下结果 -

  • MariaDB 子句用于在语句中从结果中删除重复项。 语法: 注意:当在子句中仅使用表达式时,查询将返回该表达式的唯一值。当您使用多个表达式在子句时,查询将返回多个表达式的唯一组合。 子句不会忽略值。因此,在SQL语句中使用子句时,结果集将包含作为不同的值。 1. 使用单一表达式示例 有一个名称为的表,有一些重复的条目。例如,就有两个学生的名字叫:。可以先来看看表的中的全部数据记录。 现在,使用子

  • 在MariaDB中,当操作需要完全匹配时,子句与语句一起使用来检索数据。它可以与,,和语句一起使用。 它用于模式匹配并返回或。用于比较的模式接受以下通配符: 通配符:匹配字符数(0或更多)。 通配符:匹配单个字符。它匹配其集合中的字符。 语法: 1. 使用%通配符(百分号通配符) 假设我们有一个表,并有以下数据。 现在想要查询那些名字以字母开头的所有学生信息,那么就可以使用条件的通配符来查找所有以

  • 在MariaDB中,子句与,,和语句一起使用来选择或更改想要更改的满足指定特定行记录。 它是一个在表名后面出现的语句。 语法 注意:子句是一个可选的子句。它可以和,,,运算符一起使用。 1. WHERE子句与单一条件 示例: 我们有一个表,里边有一些数据。假设要查询表中的小于的所有记录。 执行上面查询语句,得到以下结果 - 2. WHERE子句与AND条件 为了方便演示,这里再插入一条数据 - 当

  • 在MariaDB数据库中,子句用于按升序或降序对结果集中的记录进行排序。 语法: 注意:可以对结果进行排序而不使用属性。 默认情况下,结果将按升序()排序。 1. ORDER BY子句不使用ASC/DESC属性 在这个示例中,使用具有以下数据的表: 示例 执行上面查询语句,得到以下结果 - 在上面结果集中,可以看到是按字段从小到大(不指定或时,默认使用)来排序的。 2. ORDER BY子句使用D

  • 问题内容: 对于我因此遇到的所有Dockerfile(虽然数量不多),它们所有人都使用了子句作为现有映像的基础,即使它是。 这个条款是必需的吗?有没有子句的Dockerfile是否可能?这样创建的容器可以执行任何操作吗? 编辑 我读 没有FROM指令的Dockerfile没有父映像,称为基本映像。 https://docs.docker.com/glossary/?term=parent%20im