【问题标题】:How to solve concurrency Insert issue in SQL Server stored procedure如何解决 SQL Server 存储过程中的并发插入问题
【发布时间】:2015-05-15 02:45:52
【问题描述】:

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

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

这个存储过程在正常情况下工作正常,但是当多个用户同时调用它时,会发生主键冲突问题,同样的情况我也通过使应用程序多线程来重现。我知道这个问题与并发有关,最初我在选择查询中使用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

【问题讨论】:

  • 你听说过Auto Incremented fields
  • 你可以考虑使用sp_getapplock
  • 您是否正确阅读了我的问题? @MethodMan
  • 这是 SQL Server 具有/允许以这种方式设置自动增量的列,当您执行插入时,您确保 Id 字段是具有自动增量和正确查看功能的主键如有必要进行设置,然后在调用插入时插入除 Id 字段之外所需的所有内容,因为它将自动生成.. 还阅读一些关于基本查询和在 sql server 中设置自动增量字段的教程
  • 哥们休息一下看书

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


【解决方案1】:

尝试类似......

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 也是一个 auto-increment 。

【讨论】:

【解决方案2】:

有一个故障安全策略:SERIALIZABLE隔离并在死锁的情况下重试。 SERIALIZABLE 的定义是排除所有竞争条件的as-if-serial execution。

我不知道你为什么在那里捕获异常。所做的只是抑制错误。

【讨论】:

  • 当你有一个事务时,你就有可能事务失败(它会优雅地失败,但它可以并且在生产中偶尔会失败)并且当它发生时它离开并且事务处于半死状态(不可承诺,但仍然有活力),因此在这种情况下,将其从痛苦中解脱出来是一种很好的做法。
  • 如果你没有明确提交事务,它总是回滚。这通常是应用程序清理资源(即 1 毫秒后)的情况。将每个 sql 批处理包装在防御行为中是浪费开发时间。在这种情况下,它导致了一个错误:错误被吞没了,应用程序继续运行,却忘记了这个操作失败并且没有 tran 的事实。该应用程序现在会将有害语句发送到 tran 之外的数据库中。
  • 该示例可以添加一个 throw 以进一步通知发生了错误。当然,未提交的事务会回滚,但通过发出显式“回滚”,SQL Server 用于维护事务的内存和对象会立即释放。
【解决方案3】:

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,将确保唯一性。唯一的问题是,在回滚/失败事务的情况下,您是否会接受计数序列中的间隙......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 2012-04-18
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    相关资源
    最近更新 更多