1

假设我在一家流量很大的网上商店工作。出售的物品需求量很大,但也非常有限。我需要确保它们不会被超卖。

目前我有这样的事情:

$order->addProduct($product);
$em->persist($order);
if($productManager->isAvailable($product)){
    $em->flush();
}

但是,如果在很短的时间内收到两个订单,我想这仍然允许超卖产品。还有哪些其他可能性可以确保产品绝对不会超卖?

4

2 回答 2

1

您需要在事务中使用悲观锁。

假设您的Product实体有一个count包含剩余项目数的字段。用户购买商品后,您可以减少该字段。

在这种情况下,您需要一个悲观的写锁。基本上,它会锁定一行,使其不被其他尝试获取悲观锁的进程读取和/或更新。这些进程保持锁定状态,直到锁定行的事务通过提交或回滚或超时结束。

所以,你开始一个事务,获取一个锁,检查是否有足够的物品,将它们添加到订单中,减少物品的数量并提交事务:

$em->beginTransaction();

try {
    $em->lock($product, LockMode::PESSIMISTIC_WRITE);

    if ($product->getCount() < $numberOfItemsBeingPurchased) {
        throw new NotEnoughItemsLeftInStock;
    }

    $order->addItem($product, $numberOfItemsBeingPurchased);
    $product->decreaseCount($numberOfItemsBeingPurchased);

    $em->commit();
} catch (Exception $e) {
    $em->rollback();
    throw $e;
}

我建议在这里抛出异常,因为 2 个用户同时购买最后一件商品是一种特殊情况。当然,在运行此代码之前,您应该使用某种项目计数检查——验证约束或其他东西。因此,如果一个用户通过了那张支票,但另一个用户在支票之后和当前用户实际购买之前购买了最后一件物品,这是一种特殊情况。

另请注意,您应该在单个HTTP 请求期间开始和结束事务。我的意思是,不要一个 HTTP 请求中锁定一行,等待用户完成购买,然后再释放锁定。如果您希望用户能够在他们的购物车中保留物品一段时间——就像在现实世界的购物车中一样——使用其他方式,比如通过仍然减少库存物品的数量为用户保留产品一段时间,以及如果在超时后通过添加该数量的项目来释放它。

于 2013-09-16T16:26:43.123 回答
-1

有一个完整的章节Doctrine2讨论并发,这正是您所需要的。

您需要编写一个事务性自定义查询,并在查询期间锁定您的表。这一切都在这里解释:事务和并发

于 2013-09-16T14:05:03.477 回答