如何解决并发在SQL Server存储过程中插入问题

时间:2015-03-12 19:11:50

标签: c# sql asp.net sql-server stored-procedures

我有一个存储过程,它以@id作为输入参数。表格StudentId_ColumnCount_Column上有主键。如果表中存在给定id的任何记录,那么我从表中选择最大Count_Column并通过将max Count_Column递增1而使用零值来插入新行。

我在WCF服务中从ado.net代码调用此存储过程,并且从asp.net Web应用程序调用此服务。

这个存储过程在正常情况下工作正常,但是当多个用户同时调用它时,会发生主键冲突问题,同样的情况我也通过使应用程序多线程重现。我知道这个问题与并发有关,最初我在select查询中使用with(nolock),但我现在已经删除了它。

某处我已经读过,通过设置事务隔离级别,它可以解决,但是当我尝试时,我有些得到回滚事务异常。

请告诉我这个问题的任何有效解决方案。

declare @iCount = 0;

if exists(select 'x' from Student with(nolock) where Id_Column = @iId)
begin
    set @iCount = (select max(Count_Column) 
                   from Student
                   where Id_Column = @iId)
end

insert into Student
values(@id, @iCount + 1);

第二个解决方案:

begin try
set transaction isolation level serializable
begin transaction
    declare @iCount = 0;

    if exists(select 'x' from from Student with(nolock) where Id_Column = @iId)
    begin
        set @iCount = (select max(Count_Column) 
                       from Student
                       where Id_Column = @iId)
    end

    insert into Student
    values(@id, @iCount + 1);

    commit transaction
end try
begin catch
    rollback transaction
end catch

3 个答案:

答案 0 :(得分:3)

尝试类似......

BEGIN TRY
  BEGIN TRANSACTION;
     DECLARE @iCount INT;

    IF EXISTS(SELECT 1 FROM Student WITH(UPDLOCK,HOLDLOCK) WHERE Id_Column = @iId)
    BEGIN

      select @iCount = ISNULL(max(Count_Column), 0) + 1 
      from Student WITH(UPDLOCK,HOLDLOCK) where Id_Column = @iId

         insert into Student
         values(@id, @iCount);
    END
  COMMIT TRANSACTION;
END TRY

BEGIN CATCH

 IF (@@TRANCOUNT <> 0)
     ROLLBACK TRANSACTION;

END CATCH

重要提示

您应该在此处使用Identity列来处理自动增量值。如果您使用的是sql server 2012或更高版本,您还可以选择使用Sequence Object自动增量。

答案 1 :(得分:0)

有一个故障安全策略:SERIALIZABLE隔离并在发生死锁时重试。 SERIALIZABLE的定义是as-if-serial执行,它排除了所有竞争条件。

我不知道为什么你会在那里捕捉异常。所有这一切都是为了抑制错误。

答案 2 :(得分:0)

Oracle有序列,MS跟着Sql Server 2012,其实现调用序列对象,而不是特定于表。

CREATE SEQUENCE   
Schema.MySequence
AS int
INCREMENT BY 1 ;

然后您可以按照以下方式使用它:

DECLARE @iCount int ;
SET @iCount = NEXT VALUE 
FOR Schema.MySequence;

这与Oracle中的nextval类似,将确保唯一性。唯一的问题是,如果回滚/失败的交易,你是否会接受计数序列中的差距......