Конфликт 2+ одновременных запросов во время MySQL вставка / обновление - PullRequest
0 голосов
/ 11 марта 2020

У меня есть сайт со средним трафиком c с базой данных MySQL, и я вижу случайные Дублирующиеся записи ошибки, когда 2+ одновременных запроса пытаются обновить одну и ту же строку.

Мы используем Perl / DBI для доступа к базе данных.

Perl псевдокод i sh:

$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 UPDATE и SELECT ... LOCK IN SHARE MODE, как описано здесь:

MySQL InnoDB: разница между `FOR UPDATE` и` LOCK IN SHARE MODE`

но поскольку мы вставляем новую строку во время запроса # 1, нет строки для блокировки до вставки, которая блокировала бы запрос № 2.

После прочтения вышеуказанной ссылки и другие ресурсы в Интернете, я действительно не знаю, что делать дальше, что будет работать надежно, без тупиков и других подобных вещей.

Идеи? Помогите? Спасибо!

1 Ответ

1 голос
/ 15 марта 2020

Как указано здесь: https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb -record-locks

select for update должно решить эту проблему, поскольку, как утверждается, она также предотвращает вставку, первый запрос с select for update должен заблокировать второй запрос select for update с тем же идентификатором.

Другое требование состоит в том, что уровень изоляции должен быть как минимум прочитанным, зафиксированным

...