[2+并发请求在MySQL插入/更新期间发生冲突

问题描述 投票:0回答:1

我有一个带有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,因此我们尝试再次插入,并出现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。

在阅读了上面的链接和网络上的其他资源之后,我真的不知道接下来该怎么做才能可靠地工作,而不会出现死锁和其他类似的可怕事情。

想法?救命?谢谢!

mysql transactions dbi concurrentmodification
1个回答
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

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

© www.soinside.com 2019 - 2024. All rights reserved.