【问题标题】:eCommerce items management电子商务项目管理
【发布时间】:2015-02-02 09:04:57
【问题描述】:

我正在创建一个销售门票的应用程序,我有一张可用门票的表格

id       | serial      | sold
-------------------------------
1        | 000001      | false
2        | 000002      | false
3        | 000003      | true

当用户请求一张票时,应用会选择一张票并将其标记为已售出。

我的问题是:如何管理这张表并防止将同一张票卖给不同的用户,而知道同时会有数百个请求?

我正在考虑锁定,但是当该行被锁定以进行事务时,如果另一个事务请求锁定,它必须等到第一个事务完成并将该行更新为已售出,然后第二个事务将返回该票已售出。现在,如果发现该行被锁定,我如何将第二个事务重定向到不同的行?

编辑

根据 Paul Zahra 的评论,我创建了一个存储过程

DECLARE @id varchar(MAX)        
SELECT @id = (
    SELECT top(1) [Id]
    FROM   [dbo].[AvailableItems] WITH (ROWLOCK, UPDLOCK)
    WHERE  ([Sold] = 0) )

UPDATE [dbo].[AvailableItems]
SET [Sold] = 1
WHERE [Id] = @id

SELECT [Id], [Serial], [CreateDate], [AvailableTo]
FROM   [dbo].[AvailableItems]
WHERE  ([Id] = @id)

但是我需要确定如果两个交易同时发生,第二个交易会发生什么?它会跳过这一行并移动到下一行吗?还是等待第一个事务完成然后选择同一行?

【问题讨论】:

  • 有人买票的时候能直接生成连载吗?检查该系列尚未售出并将其标记为已售出。
  • 不行,因为我要导出表格到入口系统
  • 好的。为什么不简单地循环表格直到你达到最大 id 值,寻找一个 sold = false ?这有点低效,那为什么不'select * where sold = false'呢?如果由于竞争条件而失败,那么循环?
  • 这很好,但是会延迟请求,锁定怎么样,你能帮我写代码吗?
  • 读一读...现在在这里*.com/questions/3767328/how-to-perform-a-row-lock 检测一行是否被锁定,恐怕是非常困难*.com/questions/11464317/…

标签: transactions sql-server-2008-r2


【解决方案1】:

我会使用一个简单的对象锁定项目并确保它是私有/静态的。这将允许其状态在类的所有实例之间共享,同时防止从类外部修改对象。

有关错误处理和锁定,请参阅

In C# how can I safely exit a lock with a try catch block inside?

支持 //Log 异常锁将被自动释放的语句。

该代码尚未经过测试,但它应该会给您一个良好的开端。

public class NextTicketinSequence
{
    private static int _LastTicketNumber;
    private static object _TicketLock;

    public static NextTicketinSequence()
    {
         NextTicketinSequence._LastTicketNumber = 0;
    }

   public static int GetNextTicketNumber()
   {
     int ticketId = 0;

      try
      {
        lock(_TicketLock)
        {
            NextTicketinSequence.LoadLastTicketSold();
            ticketId = NextTicketinSequence._LastTicketNumber + 1;
        }
      }
      Catch(Exception ex)
      {
            //Log exception lock will be released automaticaly
      }

      return ticketId
   }

  public static void LoadLastTicketSold
  {
      //Code to get the last ticket marked as sold
  }
}

【讨论】: