【问题标题】:Mysql SELECT FOR UPDATE - strange issueMysql SELECT FOR UPDATE - 奇怪的问题
【发布时间】:2011-02-02 04:43:50
【问题描述】:

我有一个关于 MySQL 锁定功能的奇怪问题(至少对我来说 :))。

我有一张桌子:

create table `test` (  
  `id` int(11) NOT NULL AUTO_INCREMENT,  
  PRIMARY KEY (`id`)  
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1

有了这些数据:

+----+
|编号 |
+----+
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 10 |
| 11 |
| 12 |
+----+

现在我有 2 个客户端,这些命令在开始时执行:

设置自动提交=0;
设置会话事务隔离级别可序列化;
开始;

现在是最有趣的部分。第一个客户端执行此查询:(意图插入 id 等于 9 的行)

SELECT * from test where id = 9 FOR UPDATE;
空集(0.00 秒)

然后第二个客户端做同样的事情:

SELECT * from test where id = 9 FOR UPDATE;
空集(0.00 秒)

我的问题是:为什么第二个客户端不阻塞?第一个查询应该设置了独占间隙锁,因为已经使用了 FOR UPDATE 并且第二个客户端应该阻塞。

如果我错了,有人可以告诉我如何正确地做吗?

我使用的MySql版本是:5.1.37-1ubuntu5.1

【问题讨论】:

  • 它不会阻塞,因为您已成功锁定 行。

标签: mysql locking blocking


【解决方案1】:

因为在那个时候,返回(空)结果是安全的 - 没有为 id=9 的记录设置锁定,因为它不存在,因此无法更新 - 我认为你不能在这种情况下依靠 innodb 设置读锁。不过,它应该在 id=9 上设置写锁定。

如果稍后,其中一个事务确实更新了表,并与另一个事务接触了相同的数据 - 更新可能会阻塞其中一个事务,如果另一个事务提交该数据,则稍后会失败。在这种情况下事务失败是完全正常的 - 让您处理它 - 这通常是重试事务的问题。

如果有一条 id=9 的记录,您可能会看到 2.select 阻塞直到第一个事务完成,因为现在有一条记录必须被读取锁定,以防第一个事务决定更新该行。

【讨论】:

  • 是的,如果有 id=9 的记录,则第二个选择块。那么我应该如何“保留” id 9 呢?我遇到了这个问题,因为我存储了成对的对象之间的一些关系,并且对于给定的一对对象,我只想要一行。
  • 很难准确地说出您要做什么。您的一项交易会创建 id=9 吗?在这种情况下,其他事务将失败/回滚。
  • 我正在尝试在 Java 中实现类似的东西: if (!rowExists(9)) { createRowWithId(9); } else { 返回“错误”;并且我得到了重复的 ids...
  • 对于这种情况,似乎对 id 的唯一约束应该处理它。对于更一般的情况,它可能会更复杂。虽然你可以这样做 if (rowsWithId(9) == 1) { createRowWithId(9); if (rowsWithId(9) > 1) 回滚(); } ,事务将确保只有一个多个并发的回滚。
  • 我有同样的问题,除了我不能使用唯一键技巧。有什么想法吗?
【解决方案2】:

【讨论】:

  • 但我不想锁定整个表。那样会很慢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
  • 1970-01-01
  • 2014-03-10
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多