Предотвратить одновременный доступ и вставки с MySQL и InnoDB

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

Сервер использует PHP 5.2.0, который объясняет, что я не использую класс DateTime. Моя таблица победителей - InnoDB.

Эта часть моего скрипта вызывается каждым клиентом при регистрации пользователя и определяет, выиграл пользователь или нет:

$currentTime = date("Y-m-d H:i:s");

// We're looking for the last winner in the winners table
$res = $dbh->query("SELECT date FROM winnertable ORDER BY id DESC")->fetch(PDO::FETCH_ASSOC);

if(empty($res)){
    // If there is no last winner then the user wins
    $winner = true;
}else{
    // If there is already a last winner, we check the date of win, if superior to half an hour, the user wins 
    if(strtotime($currentTime)-strtotime($res["date"])>=1800){
        // Last winner was half an hour ago : winner
        $winner = true;
    }
}

// if the winner flag is set to true, add the user id to the winners table along with the date of winning
if($winner){
    $dbh->query("INSERT INTO winnertable (id_main, date) VALUES ('".$_SESSION['id']."', '".$currentTime."')");
}

Следующая страница должна показать результат пользователю, чтобы он знал, выиграл он или нет. Так что я не могу сделать CRON работу, чтобы решить потом.

Этот сценарий имеет недостатки, поскольку при одновременном доступе к нескольким клиентам он может выиграть несколько клиентов.

Я прочитал несколько решений вокруг:

  • Использование замков: но я не знаю стоимость производительности. Кроме того, я читал, что таблицы MyIsam блокировали всю таблицу, в то время как таблицы InnoDB блокировали только выбранные строки, помеченные для обновления. И я не знаю, что делать оттуда.
  • Создание моей колонки "дата" в формате UNIQUE должно предотвратить многократное добавление одной и той же даты и кажется изящным обходным путем, но, кажется, его слишком легко доверять, и мне очень хотелось бы получить ваш совет.

2 ответа

Решение

Я наконец решил думать о проблеме по-другому и решил опубликовать решение, поскольку это может помочь некоторым людям.

Я прочитал на уровне блокировки строк таблицы InnoDB и сделал следующее:

Stmt # 1: ОБНОВЛЕНИЕ таблицы победителей SET id_main=[id] ГДЕ date_add(next_date, INTERVAL X SECOND)

Если PDO возвращает измененную строку ($stmt->rowCount()), то это победитель, и вы должны вставить новую строку в победительскую таблицу для следующего победителя:

Stmt # 2 (условно): INSERT INTO Победитель SET SET next_date=NOW()

Спасибо за помощь!

Что вы можете сделать, это добавить еще один этап к ситуации, когда у вас есть еще одна временная таблица, в которой хранятся "потенциальные" победители - те, которые удовлетворяют критериям выигрыша. Затем выполните SELECT для этого в качестве вторичного действия, упорядочив по дате, индексируйте в порядке возрастания с пределом один. Возьмите это значение и добавьте его в таблицу реальных победителей, используйте его в качестве основы для расчета следующего выигрыша и очистите временную таблицу. Таким образом, вы должны получить только одного победителя. Конечно, это добавит накладных расходов, но это будет происходить каждые полчаса, поэтому я не думаю, что это должно быть слишком большой проблемой.

Другие вопросы по тегам