我有一个网站,用户可以在其中购买门票,但门票数量通常是有限的,而且要尽快购买。我正在尝试实现一个托管系统,以便用户可以单击他们想要 x 数量的票,此时我会将它们置于托管状态。这给了他们几分钟的时间来输入他们的信用卡信息并完成购买。
我有三个相关表:事件、门票和托管。事件表中的一行描述了事件本身,包括可用门票的最大数量。
门票表包含以下内容:
user_id:购买门票的用户
number_of_tickets:他们购买了多少张票
event_id:相关事件
托管表包含以下内容:
user_id:购票过程中的用户
number_of_tickets:他们想要多少张票
event_id:相关事件
目前,我执行了三个 MySQL 查询,一个查询最大票数,一个查询售出的票数,一个查询已托管的票数。然后我计算:
$remaining_tickets = $max_tickets - $tickets_sold - $tickets_in_escrow;
if ($remaining_tickets >= $tickets_desired)
{
beginEscrow($user_id, $event_id, $tickets_desired);
}
else
{
echo "Error: not enough ticket remain.";
}
我的问题是有可能不止一个用户同时执行这段代码。如果一个用户调用 beginEscrow
在另一个用户已经读取了已经在托管中的门票数量,我可能会超卖演出。
我正在为我的表使用 InnoDB 引擎,并且我已经阅读了如何使用 SELECT .... FOR UPDATE
锁定单行,但我没有更新单个行排。 beginEscrow
函数只会在托管表中插入一个新行。我计算 $tickets_in_escrow
的方法是读取具有正确事件 ID 的所有行并将每行中的门票数量相加。
也许我做错了?
我需要锁定整个表吗?
我不能是第一个编写票证托管系统的人。我已经用谷歌搜索了自己,试图找到一些关于这类事情的教程,但被淘汰了。任何想法都会有所帮助。
谢谢!
最佳答案
您的设计非常接近,但还差得远。
首先,您的事件表需要保存您的事件仍然可用的门票数量(除了您想要的其他任何东西)。
其次,您的托管表需要有一个 DATETIME 列,指示托管到期的时间。每当票据进入托管时,您都需要设置该值。
第三,将票据托管的交易需要
- 锁定事件行。
- 阅读可用门票栏。 (如果没有足够的可用则中止)
- 在托管表中插入一行
- 更新事件行以减少可用门票列。
- 解锁事件行。
第四,完成销售的 Action 需要删除escrow行,插入sold-ticket行。这并不难。
第五,你需要一个托管清理操作。这需要查找所有已过期(过去有过期日期)的托管行,并且对于每个:
- 锁定相应的事件行。
- 从托管表中读取托管票的数量
- 删除托管表行。
- 更新事件行以增加可用门票列。
- 解锁事件行。
诀窍是以正确互锁的方式维护可用门票的数量,这样用户之间的竞争条件就不会超卖您的事件。
关于MySQL原子操作和表锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11278494/