在mysql

时间:2016-04-04 03:28:37

标签: php mysql

我开发了一款可以通过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?

1 个答案:

答案 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 JOINgame_roulette并使用closed约束进行g.id IS NULL来完成的。

完成此查询后,如果需要,应用程序可以检查INSERT的结果,就像对任何其他查询一样,以了解是否插入了任何行作为结果。

注意:如果您在大型桌面上执行此操作,则应衡量查询效果和/或确保closed列上有索引,并且它已获得使用