在mysql中一次只允许打开一个条目


Only allow 1 open entry at a time in mysql

我开发了一款通过ajax与php和jquery一起工作的游戏。

本质上,当向服务器发出请求时,如果还没有打开游戏,服务器将创建一个新游戏。我的游戏数据库表如下:

id | closed | time

游戏结束后,Where closed将设置为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?

您最终得到2条记录的可能原因是,正如您所怀疑的,并发性。如果两个进程分别首先运行SELECT查询,那么它们都将获得继续运行的绿灯,并且它们都将运行INSERT。因此,您最终会得到2行(或更多!)这样的行。

传统上,您会使用事务或锁来避免这种情况,但也有一个SQL"技巧"可以让单个原子查询实现这一点:

插入game_rouletteSELECT NULL,x.closed,NOW()来自(选择"0"为关闭)xLEFT加入game_roulette g ON x.closed=g.closed其中g.id为空

只是为了解释这是如何工作的:这是一种INSERT ... SELECT语法,其中SELECT查询只有在没有game_roulette.closed'0'的行的情况下才会产生结果。这又是通过创建单行表x,然后对closed列上的game_roulette执行LEFT JOIN,并使用g.id IS NULL约束来完成的。

在完成该查询之后,如果需要,应用程序可以检查INSERT的结果,就像它对任何其他行所做的那样,以知道是否有任何行作为结果被插入。

注意:如果您在一个大表上执行此操作,则应该测量查询性能和/或确保closed列上有一个索引并且正在使用。