我开发了一款可以通过ajax与php和jquery一起使用的游戏。
基本上,当向服务器发出请求时,如果尚未打开,则服务器将创建新游戏。我的游戏数据库表如下所示:
id | closed | time
游戏结束后,关闭的位置将设置为1,时间是游戏创建时的unix时间戳。
现在重要的是,在任何给定时间,表格中只能有一场关闭= 0的游戏。
要做到这一点,我使用以下PHP代码
$query = "SELECT id FROM games WHERE closed = '0' LIMIT 1";
$result = $this->database->query($query);
if ($result->num_rows == 0) {
$query = "INSERT INTO games_roulette SET time = '".time()."'";
$result = $this->database->query($query);
return $this->database->insert_id;
} else {
return false;
}
偶尔会创建2个游戏,我相信由于2个请求在同一时间发送,因为当有2个游戏打开时,时间值总是完全相同。
我有什么方法可以100%确定桌子上永远不会有2场比赛关闭= 0?
答案 0 :(得分:1)
正如您所怀疑的那样,您最终获得2条记录的可能原因是并发性。如果2个进程首先单独运行SELECT
查询,则他们都会开始进行绿灯,并且他们都会运行INSERT
。所以你最终得到2个(或更多!)这样的行。
传统上,您使用交易或锁来避免这种情况,但也有一个SQL"技巧"这将允许单个原子查询来完成此任务:
INSERT INTO game_roulette SELECT NULL, x.closed, NOW() FROM (SELECT '0' as closed) x LEFT JOIN game_roulette g ON x.closed = g.closed WHERE g.id IS NULL
只是为了解释一下这是如何工作的:它是一种INSERT ... SELECT
语法,SELECT
查询只会产生一个结果,如果没有game_roulette.closed
所在的行'0'
。反过来,这是通过创建单行表x
,然后针对LEFT JOIN
列game_roulette
并使用closed
约束进行g.id IS NULL
来完成的。
完成此查询后,如果需要,应用程序可以检查INSERT
的结果,就像对任何其他查询一样,以了解是否插入了任何行作为结果。
注意:如果您在大型桌面上执行此操作,则应衡量查询效果和/或确保closed
列上有索引,并且它已获得使用