У меня есть сайт со средним трафиком 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.
После прочтения вышеуказанной ссылки и другие ресурсы в Интернете, я действительно не знаю, что делать дальше, что будет работать надежно, без тупиков и других подобных вещей.
Идеи? Помогите? Спасибо!