mysql - 从具有并发连接的表中获取
问题描述
我们有一个用户可以登录并玩游戏的游戏,该游戏仅适用于 6 个用户组,当 6 个用户加入一个组时,游戏开始。
我们在 mysql 中有一个名为“temp_group”的表,其中包含 user_id 和一个 random_number 现在,我们要实现的是当用户进入游戏时,我们需要插入他的 user_id 并检查有多少用户已经在具有相同 random_number 的组中,场景如下:
- 如果组中没有用户(第一个进入游戏的用户) - 我们生成一个随机数并将其插入新用户
- 组中有用户(第 2 到第 6 个用户加入游戏) - 获取该组的 random_number 并针对该用户进行更新
- 如果第 7 个用户加入游戏 - 将他视为组的新用户,并按照第 1 点进行操作。
我们创建了一个存储过程,我们首先检查
SELECT count(random_number), random_number from temp_group group by random_number HAVING COUNT(random_number) < 6 order by id ASC limit 1
如果我们得到该行,我们获取 random_number 并为新用户(第 2 - 6 个用户)更新它
如果我们没有得到该行,我们生成 random_number 并针对新用户更新它。
我们的存储过程:
BEGIN
DECLARE ch_done INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET ch_done = 1;
START TRANSACTION;
SELECT count(random_number), random_number from `temp_group` where group by random_number HAVING COUNT(random_number) < players order by temp_id ASC limit 1 into cnt, randomnumber;
IF(ch_done = 1) THEN
IF(randomnumber IS NULL) OR (cnt = 0) THEN
SET randomnumber = MD5(NOW());
SET cnt = 0;
END IF;
END IF;
INSERT INTO temp_group (`user_id`,`random_number`,`no_of_players`) VALUES(userid,,randomnumber,6) ON DUPLICATE KEY UPDATE `random_number` = randomnumber, `no_of_players` = 6;
COMMIT;
END
问题:
当我们一次测试一个用户加入游戏时,它可以正常工作。但是当我们用并发连接测试它时,相同的 random_number 被分配给超过 6 个用户。
问题是当并发连接调用存储过程时,所有存储过程都会获取选择查询并获取随机数,因此它只是为所有用户更新相同的随机数。任何人都可以帮助我如何阻止存储过程读取数据,直到上一个存储过程更新表并提交
解决方案
您的程序存在一些问题:
首先,可能是您的主要问题,您select
的问题是非锁定的,例如,如果两个并发会话同时读取表,它们可以获得相同的结果并根据相同的当前状态做出决定。例如,他们可能会将他们的球员添加到同一支球队,因为他们都假设目前只有 5 名未分配球员,从而导致球队 7 人。修复将是锁定读取,您可以通过添加for update
:
SELECT count(random_number), random_number ... into cnt, randomnumber for update;
然后第二个查询必须等到您提交第一个会话(空表除外,这可能是也可能不是这里的问题),因此,如您所愿,它“停止存储过程读取数据直到前一个存储过程更新表并提交”。
第二个问题MD5(NOW())
不是唯一的。如果您有两个并发会话,则意味着它们同时运行 - 因此MD5(NOW())
将返回相同的值,即使您的过程实际上打算创建一个新组。如果超过 6 名玩家在同一秒内加入,这可能只会成为问题,但即使是罕见的问题也是问题。
虽然您可能可以找到更好的随机化器,但我强烈建议您添加一个新表,其中包含有关该组的信息,以最简单的形式,例如 just team (id int primary key auto_increment)
。您当前的表temp_group
可能会变成类似team_user (team_id, user_id)
.
不是生成一个随机数来创建一个新团队(并同时为其分配一名球员),而是通过在球队表中添加一行来创建一个新球队,然后通过将球员插入到team_user
桌子。这样,您就可以正确地将两个逻辑步骤(创建新团队、将用户添加到团队)分成两个实际步骤。
一个适当的、规范化的数据模型使许多数据库任务变得更容易,例如你的 teamid 的唯一性基本上自然而然地出现了,而无需做任何更多的工作(即使你需要每个团队唯一的 md5 值,你也有一个简单的方法来执行它通过向团队表添加唯一键)。还假设您想要添加有关团队的任何其他信息(例如团队名称、游戏模式、创建私人/密码保护大厅的选项,或者,正如您目前似乎存储的那样,最多 6 名玩家) - 您必须在表中多次存储它temp_group
,这总是一个坏主意。
最后一个问题:
SELECT ... group by random_number ... order by temp_id
这通常是无效的,因为temp_id
没有确定性值(例如,您的意思是最多 6 个值中的哪一个)temp_id
?random_number
MySQL 仅在您禁用时才允许这样做only_full_group_by
(您不应该这样做)。例如,可以使用一个修复程序order by min(temp_id)
(取决于您想要实现的目标)。另一方面,如果添加team
-table,则可以(正确)使用group by team_id having .... order by team_id
.
推荐阅读
- magento2 - 重复运行模式...我们找不到您想要的用户的角色
- c# - HashSet 项可以更改为 Set 中的相同项
- java - Deserializing arbitrary JSON in Java with Gson and respecting integers
- python-2.7 - ImportError: cannot import name Document
- python-2.7 - how to duplicate output to terminal in bs4 in text file
- regex - Removing parentheses and everything in them with Regex
- arrays - Ionic 3 将数据插入 Firebase RTDB
- python - IDLE 如何打印 sys.stdout.encoding 不支持的字符(python 3.7)
- php - How to store data into json.file with variable name from database using php
- r - 在 R 中粘贴 5 个列组的复杂问题