根据计数有条件地将记录插入到多线程环境中的表中

时间:2014-07-11 20:39:31

标签: sql sql-server multithreading tsql

我正在编写一个T-SQL存储过程,只有当类似记录的数量低于某个阈值时才会有条件地将记录添加到表中,如下例所示。问题是这将从Web应用程序运行,因此它将在多个线程上运行,我需要确保该表永远不会有超过10个类似的记录。

该程序的基本要点是:

BEGIN
  DECLARE @c INT
  SELECT @c = count(*)
    FROM foo
    WHERE bar = @a_param

  IF @c < 10 THEN
    INSERT INTO foo
      (bar)
    VALUES (@a_param)
  END IF
END

我认为我可以通过将select语句替换为:

来解决任何潜在的并发问题
SELECT @c = count(*) WITH (TABLOCKX, HOLDLOCK)

但我很好奇是否有其他方法除了用于管理T-SQL中的并发问题的锁定提示

3 个答案:

答案 0 :(得分:1)

使用SERIALIZABLE。根据定义,它为您提供了一个错觉,即您的事务是唯一运行的事务。请注意,这可能会导致阻塞和死锁。事实上,这个SQL代码是死锁的经典候选:两个事务可能首先读取一组行,然后两个都会尝试修改这组行。锁定提示是解决该问题的经典方法。重试也有效。

答案 1 :(得分:1)

一种选择是使用sp_getapplock系统存储过程。您可以将关键部分逻辑放在事务中,并使用sql server的内置锁定来确保同步访问。

示例:

CREATE PROC MyCriticalWork(@MyParam INT)      
AS
    DECLARE @LockRequestResult INT
    SET @LockRequestResult=0

    DECLARE @MyTimeoutMiliseconds INT
    SET @MyTimeoutMiliseconds=5000--Wait only five seconds max then timeouit

    BEGIN TRAN

    EXEC @LockRequestResult=SP_GETAPPLOCK 'MyCriticalWork','Exclusive','Transaction',@MyTimeoutMiliseconds
    IF(@LockRequestResult>=0)BEGIN

        /*
        DO YOUR CRITICAL READS AND WRITES HERE
        */

        --Release the lock
        COMMIT TRAN
    END ELSE
        ROLLBACK TRAN               

答案 2 :(得分:0)

如评论中所述。为什么要尝试在多个线程上插入?您无法在多个线程上更快地写入表。

但你不需要声明

  insert into [Table_1] (ID, fname, lname)
  select 3, 'fname', 'lname' 
    from [Table_1] 
   where ID = 3 
  having COUNT(*) <= 10  

如果您需要锁定,请执行此操作

  1. 数据不是3NF
    应该使用适当的数据模型启动任何设计
  2. 为什么要排除表锁? 这很可能是最好的方法
  3. 真的,有什么机会?
    即使没有锁,你也必须在同一时间提交两个9的提交。即便如此,它也会在11点停止.10是绝对硬数吗?