MySQL原子操作和表锁定

时间:2012-06-30 23:37:47

标签: mysql locking innodb atomic escrow

我有一个网站,用户可以在这里购买门票,但门票数量通常是有限的并快速进入。我正在尝试实施一个托管系统,以便用户可以点击他们想要的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函数只会在托管表中插入一个新行。我通过读取具有正确事件ID的所有行并在每个行中添加票数来计算$tickets_in_escrow

也许我一切都错了?

我需要锁定整个桌子吗?

我不能成为第一个编写票务托管系统的人。我试图找到一些关于这种事情的教程,然后用谷歌搜索出来。任何想法都会有所帮助。

谢谢!

1 个答案:

答案 0 :(得分:9)

你的设计非常接近,但不是很完美。

首先,您的活动表需要保留您的活动仍然可用的门票数量(除了您想要的任何其他内容)。

其次,您的托管表需要有一个DATETIME列,表明托管何时到期。只要门票进入托管,您就需要设置该值。

第三,将门票存入托管的交易需要

  1. 锁定事件行。
  2. 阅读可用的故障单栏。 (如果没有足够的话可以中止)
  3. 在第三方托管表中插入一行
  4. 更新事件行以减少可用的故障单列。
  5. 解锁事件行。
  6. 第四,完成销售的行动需要删除托管行并插入售票行。这并不难。

    第五,您需要进行托管清理操作。这需要查找已过期的所有托管行(过去有过期日期),以及每个托管行:

    1. 锁定相应的事件行。
    2. 从托管表中读取托管门票的数量
    3. 删除托管表格行。
    4. 更新事件行以增加可用的票证列。
    5. 解锁事件行。
    6. 诀窍是以正确互锁的方式维护可用票证的数量,因此用户之间的竞争条件不会超出您的事件。