MySQL insert/update 期间 2+ 个并发请求发生冲突

2+ concurrent requests conflict during MySQL insert/update

我有一个带有 MySQL 数据库的中等流量网站,当 2+ 个并发请求尝试更新相同内容时,我偶尔会看到 重复条目 错误排。

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

Perl 伪代码:

$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

对于 repeat/rephrase 这个问题,假设请求 #1 出现了。插入行。在睡觉时,请求 #2 出现; $row 是 undef 因为我们还没有提交请求 #1,所以我们再次尝试 INSERT,并得到 Duplicate entry 错误。

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

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

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

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

阅读上述 link 和网络上的其他资源后,我真的不知道下一步该尝试什么才能可靠地工作,没有死锁和其他类似的可怕事情。

想法?帮助?谢谢!

如此处指定: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

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