【发布时间】:2013-09-08 13:24:00
【问题描述】:
问题
我正在尝试弄清楚如何在数据库中正确设置事务,并考虑潜在的延迟。
设置
在我的示例中,我有一个 users、keys 表,其中每个用户可以有多个密钥,还有一个 config 表,它指示每个用户可以拥有多少个密钥。
我想运行一个存储过程:
- 确定是否允许给定用户请求密钥。
- 获取可用的、无人认领的密钥。
- 尝试为给定用户兑换密钥。
该过程的伪代码是:
START TRANSACTION
(1) CALL check_permission(...,@result);
IF (@result = 'has_permission') THEN
(2) SET @unclaimed_key_id = (QUERY FOR RETURNING AVAILABLE KEY ID);
(3) CALL claim_key(@unclaimed_key_id);
END IF;
COMMIT;
我遇到的问题是,当我在步骤1 之后模拟延迟时(通过使用SELECT SLEEP(<seconds>)),当给定用户只有兑换一个权限时,他们可以兑换多个密钥,通过在第一个过程完成睡眠之前在多个会话中运行该过程(这也是模拟延迟)
这是the Tables 和the Procedures 的代码 (注意:对于这个小例子,我没有考虑索引和外键,但显然我在实际项目中使用它们)。
要查看我的问题,只需在数据库中设置表和过程,然后打开两个 mysql 终端,并在第一次运行:
CALL `P_user_request_key`(10,1,@out);
SELECT @out;
然后在第二次运行时快速(你有 10 秒):
CALL `P_user_request_key`(0,1,@out);
SELECT @out;
两个查询都将成功返回 key_claimed 并且用户 Bob 最终将分配给他 4 个键,尽管 config 中的最大值设置为每个用户 3 个。
问题
- 避免此类问题的最佳方法是什么?我正在尝试使用事务,但我觉得它不会专门帮助解决这个问题,并且可能执行此错误。
- 我意识到解决问题的一种可能方法是将所有内容封装在一个大型更新查询中,但我更愿意避免这种情况,因为我喜欢能够设置单独的过程,其中每个过程仅用于完成一项任务。
- 此示例背后的数据库旨在供许多(数千)并发用户使用。因此,如果一位用户尝试兑换代码不会阻止所有其他用户兑换代码,那将是最好的。如果另一个用户已经领取了密钥,我可以将我的代码更改为仅尝试再次兑换,但绝对不应该发生用户在只有获得一个权限的情况下可以兑换两个代码的情况。
【问题讨论】:
标签: mysql stored-procedures transactions latency