【问题标题】:2+ concurrent requests conflict during MySQL insert/updateMySQL插入/更新期间2+并发请求冲突
【发布时间】:2020-03-11 19:44:30
【问题描述】:

我有一个带有 MySQL 数据库的中等流量网站,当 2 个以上的并发请求尝试更新同一行时,我偶尔会看到 Duplicate entry 错误。

我们使用 Perl/DBI 来访问数据库。

Perl'ish 伪代码:

$dbh->begin_work;

my $row = $dbh->selectrow_hashref( "SELECT * FROM mytable WHERE id=$some_id" );

if ( defined($row) ) {

   # ... do stuff; uses $row ...

   $dbh->do( "UPDATE mytable SET ... WHERE id=$some_id" );

}
else {

  # ... do other stuff, different from above ...

  $dbh->do( "INSERT INTO mytable SET id=$some_id, ... " );

  sleep 30; # added for emphasis
}

$dbh->commit;

id 列显然是 unique

要重复/重新表述问题,假设出现请求 #1。插入行。在睡眠时,请求 #2 出现; $row 是 undef,因为我们还没有提交请求 #1,所以我们再次尝试 INSERT,并得到 Duplicate entry 错误。

我明白为什么会这样——因为我们没有锁定。这是背景。问题是如何在这种背景下实现锁。

不幸的是,INSERT INTO ... ON DUPLICATE KEY UPDATE... 不起作用,因为我们根据$row 的存在做的事情略有不同。

我查看了SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE,如下所述:

MySQL InnoDB: Difference Between `FOR UPDATE` and `LOCK IN SHARE MODE`

但由于我们在请求 #1 期间插入新行,因此在插入之前没有要锁定的行,这将锁定请求 #2。

在阅读了上面的链接和网络上的其他资源之后,我真的不知道接下来该尝试什么才能可靠地工作,没有死锁和其他类似的可怕事情。

想法?帮助?谢谢!

【问题讨论】:

  • 你没有锁定数据库,你期望什么?
  • 这就是我问这个问题的原因:)...如何最好地锁定该行。
  • "...所以我们尝试再次插入,并得到重复输入错误。" ——没错,这就是它应该做的。你期待什么?
  • “id”不应该由INSERT操作本身产生吗?为什么要在数据库之外生成 id?似乎是人造的。
  • 似乎select for update 解决了这个问题,因为它声称也可以防止插入:dev.mysql.com/doc/refman/8.0/en/…select for update 是否在您的测试中以相同的 id 阻止 select for update

标签: mysql transactions dbi concurrentmodification


【解决方案1】:

此处指定:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-record-locks

select for update 应该可以解决这个问题,因为它声称也可以防止插入,第一个带有 select for update 的请求应该阻止第二个带有相同 ID 的请求 select for update

另一个要求是隔离级别必须至少是读取提交

【讨论】:

  • 再次感谢。我使用与问题类似的代码进行了实际测试,并且可以确认 SELECT ... FOR UPDATE 在具有相同 ID 的未提交 INSERT 之后确实读取了锁。
  • @yahermann:我的意思是 select for updateselect for update 具有相同的 id:请求#1 select for update,请求#1 休眠,请求#2 select for update,请求#2 块.不需要执行insert,可以确认一下吗?
  • @yahermann:另一个要求是隔离级别必须至少是读取提交
  • 这正是发生的事情。第二个进程中的select for update 等待/阻塞这 30 秒,直到第一个进程中的事务提交,然后第二个进程读取第一个进程插入的行。
  • @yahermann:谢谢,我现在对答案很有信心。如果您将答案标记为已接受,我们将不胜感激。
猜你喜欢
  • 2018-07-10
  • 2018-01-30
  • 2019-08-07
  • 1970-01-01
  • 2017-04-03
  • 2014-04-24
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多