我有一个rounds
mysql表(innodb),用于跟踪给定用户播放的所有游戏。它包含以下列:id(int),userId(int),gameId(int),status(int)。
Id是表的主键,userId和gameId表示其他表的外键,并且在状态中int值表示3种不同的状态,如果游戏正在进行,完成,错误。
在上一轮比赛仍在进行之前,不允许一名用户继续进行新的比赛。
我的代码执行以下操作:
SELECT id FROM rounds WHERE userId = <user> AND gameId = <game> AND status = <in progress> LIMIT 1
如果select返回结果,则抛出错误。否则我正在创造新一轮:
INSERT INTO rounds (userId, gameId, status) VALUES (<user>, <game>, <in progress>)
;
select和insert之间没有其他代码,并且大多数时候一切似乎都正常工作并创建新的游戏回合,而前一个仍在进行中,导致错误。 但是当我在大负载下测试并发性和性能的代码时,有些轮可以同时插入。 (我使用的是简单的节点脚本,它可以异步发送200个请求。)
您有什么想法我应该如何更改代码,以便在所有情况下我最多有1个活动轮次?
我似乎陷入了这个问题,我知道必须有简单的解决方案买我只是看不到它。 :(
非常感谢任何帮助!
PS:我尝试过使用INSERT ... INTO rounds SELECT ... FROM ROUNDS WHERE NOT EXISTS (SELECT id FROM rounds WHERE userId=<user> AND gameId=<game> AND status=<in progress>)
,但这会导致死锁......
编辑: 代码看起来像这样:
在游戏中:
public function play() {
$gamesInProgress = $this->repo->getGamesInProgress($this->user->getId(), $this->id, self::STATUS_IN_PROGRESS);
if($gamesInProgress) throw \Exception('Only one active game is allowed.');
$this->createGame();
// some other code
}
在回购中:
public function checkForGamesInProgress($userId, $gameId, $status) {
$stmt = $this->dbh->prepare('SELECT `id` FROM `rounds` WHERE `userId`=:userId AND `gameId`=:gameId AND `status`=:status LIMIT 1');
$stmt->prepare([
'userId' => $userId,
'gameId' => $gameId,
'status' => $status
]);
return $stmt->fetchColumn();
}
答案 0 :(得分:0)
使用“复合键”是一个好习惯。这样,数据库确保不插入重复的行。但是,当(意外地)尝试插入重复的条目时,您将不得不处理数据库错误。
我知道你已经尝试过了,但是当你运行它时会发生什么?
INSERT INTO rounds (userId, gameId, status)
SELECT <user>, <game>, '<in progress>' FROM rounds WHERE NOT EXISTS(
SELECT id FROM rounds WHERE userId = <user> AND gameId = <game> AND status = '<in progress>' LIMIT 1)