0

我有以下问题:

我们系统的产品在发布时只允许购买 X 次。购买时,中央采购算法会检查存在多少订单,如果低于 X 则继续购买。

在伪 C# 代码中:

public class OrderMethods
{

    public static Purchase(Product product, Client client)
    {

        int purchases = /* count order records of this product */;

        if(purchases>=MAX_ORDERS) throw PurchaseException();

        /* perform purchase by inserting order record in database */

     }

}

问题是,有时当对某种产品的需求量很大时,会同时发生大量请求,并且注册的数量超过 MAX_ORDERS。这种情况大约每年发生一次:(。

解决这个问题的最佳解决方案是什么?我正在使用 ASP.NET/C#、Ling2SQL 和 MSSQL。我们每天有 1000> 个订单。按照请求的顺序处理订单很重要。

到目前为止我想出的解决方案:

  • 一个全局互斥锁?

  • 每个产品一个互斥体存储在哈希表中,访问函数如下:

    private Mutex GetPurchaseMutex(Guid productId)
    {
    
    if (mutexTbl[productId] == null)
        {
            mutexTbl[productId] = new Mutex();
        }
        return (Mutex)mutexTbl[productId];
    }
    

其中 mutexTbl 是一个哈希表。在这里,我还没有弄清楚如何以一种好的方式丢弃旧的互斥锁。

  • 在 Order 表上使用 T-SQL INSERT 触发器来检查有多少订单:

    CREATE TRIGGER Triggers_OrderInsertTrigger ON Orders AFTER INSERT AS IF /* 检查是否有很多订单 */ BEGIN RAISERROR ('Too many orders', 16, 1); 回滚交易;返回结束;

但我不太喜欢这两种解决方案。你会如何解决这个问题?

4

3 回答 3

4

当可以通过事务保护它时,我会说将此逻辑移至数据库层。

检查已下订单的数量,如果可以,则在同一交易中下新订单。在此期间,新请求的事务(查询已下订单的数量)将暂停,直到第一个请求完成。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRANSACTION

DECLARE @OrderCount int
SELECT @OrderCount = COUNT (ID) FROM [Order] WHERE ProductID = '234323'

IF (@OrderCount < @Threshold)
BEGIN
    INSERT INTO [Order] (ID, ProductID, ...)
    VALUES (NEWID(), '234323', ...)

    COMMIT TRANSACTION
    RETURN 0
END
ELSE
    ROLLBACK TRANSACTION
    RETURN 1
END
于 2010-04-27T11:55:19.287 回答
3

这个问题听起来很像我在这里描述的“只有两个未发货的订单”问题。(警告,这篇文章相当长,但信息量很大)。但我认为你的选择基本上是:

  • 将完整的业务逻辑(包括验证)放入您的数据库中(就像 Developer Art 所写的那样)。
  • 使用可序列化事务并在您的业务层中用 C# 编写此逻辑,包括.Count().
  • 什么也不做,让它每年失败一次,然后清理烂摊子。

虽然您可能会觉得最后一个选项很有趣,但这实际上是一个严肃的选项。解决这种并发问题很困难,而且可能无处不在。它可能会迫使您进行重大的架构更改。此外,COUNT(*)解决方案的有效性取决于该特定表中的索引。在数据库中添加索引(出于性能原因)可能会意外地改变此解决方案的有效性和正确性。因此,从业务角度来看,每年修复一次数据库中的问题可能会便宜得多。当然,我无法猜测成本是多少,每次客户能够购买比您的业务更多的订单时。这由您(或您的企业)决定。

于 2010-04-27T13:30:17.753 回答
1

如果您不想在数据库级别处理此问题,我将创建一个类来存储每种产品的购买计数,将这些计数存储在表中,并将它们锁定在您的购买方法中。这类似于您的 mutex 方法,但您允许 .NET 为您处理锁定

public class PurchaseCounter(
{
    public Guid Product {get; set; } 
    public int MaxOrders {get; set; } 
    public int int CurrentOrders {get; set; } 
 }

public static bool Purchase(Product product, Client client)
{

    PurchaseCounter counter = purchaseCounterDictionary[product.ProductGuid];

    lock(counter)
    {
        if( counter.CurrentOrders < counter.MaxOrders )
        {
             //do logic to place order
             counter.CurrentOrders++;
             return true;  
        } 
        else
        {
             return false;
        }
    }

 }

}

于 2010-04-27T14:07:41.567 回答