2

我有一个包含这些列的表:[user_id][game_id] 当两个玩家加入游戏时,我需要关闭游戏。我使用了这段代码:

if(mysql_num_rows(mysql_query("SELECT user_id FROM live_games WHERE game_id = '$gid'"))<2){
mysql_query("INSERT INTO live_games (user_id, game_id) VALUES ('$uid', '$gid')");
echo "You have joined the game";
}else{
echo "Table is full";
}

该代码将只允许两个玩家注册,但有时当网站上的用户过多时,此条件将不起作用,并且会在表格中添加三个用户。我该如何解决?

4

3 回答 3

4

你有几个选择:

  1. 如果使用 InnoDB 存储引擎,请在与语句SELECT ... FOR UPDATE相同的事务中执行锁定读取。INSERT

    使用 PDO:

    $dbh = new PDO("mysql:dbname=$dbname;charset=utf8", $username, $password);
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    
    $dbh->beginTransaction();
    $qry = $dbh->prepare('
      SELECT COUNT(*) FOR UPDATE FROM live_games WHERE game_id = ?
    ');
    $qry->execute([$gid]);
    
    if ($qry->fetchColumn() < 2) {
      $qry = $dbh->prepare('
        INSERT INTO live_games (user_id, game_id) VALUES (?, ?)
      ');
      $qry->execute([$uid, $gid]);
      if ($qry->rowCount() && $dbh->commit()) echo 'You have joined the game';
    } else {
      $dbh->rollBack();
      echo 'Table is full';
    }
    
  2. 更改表结构,使有两列,player1并且player2(最初NULL)和 perform UPDATE live_games SET player2 = ? WHERE game_id = ? AND player2 IS NULL,然后检查受影响的行数;或者

  3. 更改表结构,以便有一个额外的playerNumber列,然后创建一个复合UNQUE索引(game_id, playerNumber)

于 2012-09-16T15:15:00.993 回答
0

您正在运行两个查询以查看是否有人可以进入游戏。

完全有可能三个玩家同时点击加入,并且 SELECT 查询返回游戏(当前)有 0 个玩家;所以他们都被允许进入 - 然后更新开始,并阻止其他玩家加入。

如果要修复它,则需要找到另一种方法-您可以添加文件锁,以便一次只运行一个 SELECT 查询,或者我相信将其包装在事务中也可以-这基本上将两个查询作为一个实体运行。

于 2012-09-16T15:11:14.820 回答
0

您需要使用LOCK TABLES语句将查询括起来,如下所示:

mysql_query("LOCK TABLES live_games WRITE");

if(mysql_num_rows(mysql_query("SELECT user_id FROM live_games WHERE game_id = '$gid'"))<2){
mysql_query("INSERT INTO live_games (user_id, game_id) VALUES ('$uid', '$gid')");
echo "You have joined the game";
}else{
echo "Table is full";
}

mysql_query("UNLOCK TABLES");

这将强制对您的表进行串行访问,从而避免不同用户同时执行相同代码的并发问题。

于 2012-09-16T15:14:35.910 回答