我有一个包含此列的表:[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";
}
代码只允许两个玩家注册,但有时当网站中的用户太多时,这种情况将无效,三个用户将添加到表中。 我该怎么办?
答案 0 :(得分:4)
您有几个选择:
如果使用InnoDB存储引擎,请在与INSERT
语句相同的事务中使用SELECT ... FOR UPDATE
执行锁定读取。
使用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';
}
更改表格结构,以便有两列player1
和player2
(最初为NULL
)并执行UPDATE live_games SET player2 = ? WHERE game_id = ? AND player2 IS NULL
,然后检查受影响的数量行;或
更改表格结构,以便有一个额外的playerNumber
列,然后在UNQUE
上创建一个复合(game_id, playerNumber)
索引。
答案 1 :(得分:0)
您正在运行两个查询以查看是否有人可以参加游戏。
完全有可能三个玩家同时点击加入,并且SELECT查询返回游戏(当前)有0个玩家;所以他们都被允许进入 - UPDATE然后踢,并阻止其他玩家加入。
如果你想修复它,你需要找到另一种方法 - 你可以添加一个文件锁,这样一次只能运行一个SELECT查询,或者我相信将它包装在一个事务中也工作 - 基本上将两个查询作为单个实体运行。
答案 2 :(得分: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");
将强制对表执行串行访问,从而避免不同用户同时执行相同代码时出现并发问题。