Предотвратить одновременный доступ и вставки с 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 для этого в качестве вторичного действия, упорядочив по дате, индексируйте в порядке возрастания с пределом один. Возьмите это значение и добавьте его в таблицу реальных победителей, используйте его в качестве основы для расчета следующего выигрыша и очистите временную таблицу. Таким образом, вы должны получить только одного победителя. Конечно, это добавит накладных расходов, но это будет происходить каждые полчаса, поэтому я не думаю, что это должно быть слишком большой проблемой.