我目前正在制作一个立即获胜的游戏,规则如下:第一个在最后一个注册后至少半小时注册的用户获胜。所以用户每半小时就可以中奖一次。如果几个小时内没有人注册,则第一个注册的人获胜。
服务器使用 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 表只锁定了标记为更新的选定行。我不知道接下来该做什么。
- 使我的“日期”列唯一应该可以防止多次插入同一日期,这似乎是一个优雅的解决方法,但似乎有点太容易被信任,我真的很想听听您的建议。
最佳答案
您可以做的是在这种情况下添加一个进一步的阶段,从而您有另一个临时表来存储“潜在”获胜者 - 满足获胜标准的获胜者。然后对其运行 SELECT 作为辅助操作,按日期排序,索引按升序排列,限制为 1。将该值添加到真正的获胜者表中,并将其作为下一次获胜计算的基础,并清除临时表。这样你就只能得到一个获胜者。当然,这会增加开销,但它只会每半小时发生一次,所以我认为这不应该是一个太大的问题。
关于php - 防止 MySQL 和 InnoDB 并发访问和插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19034088/