【问题标题】:Deadlock in SQL ServerSQL Server 中的死锁
【发布时间】:2017-05-31 06:24:59
【问题描述】:

我正面临僵局

在锁定资源上与另一个进程死锁,并且已被 被选为死锁牺牲品。

问题在 SQL-Server 中,因为我通过针对特定列选择最大 id 在数据库中插入数据,然后添加一个增量以获得将插入记录的值。 我正在调用一个过程,如下所述:

CREATE
 PROCEDURE [dbo].[Web_GetMaxColumnID]
 @Col_Name nvarchar(50)        
AS        
BEGIN       
-- SET NOCOUNT ON added to prevent extra result sets from  
 -- interfering with SELECT statements.        
 DECLARE @MaxID BIGINT;              
 SET NOCOUNT ON;   
 -- Insert statements for procedure here        
 BEGIN  
  BEGIN TRAN              
  SET @MaxID = (  
          SELECT Col_Counter  
          FROM   Maintenance_Counter WITH (XLOCK, ROWLOCK)  
          WHERE  COL_NAME = @Col_Name  
      )  

  UPDATE Maintenance_Counter  
  SET    Col_Counter     = @MaxID + 1  
  WHERE  COL_NAME        = @Col_Name  
 COMMIT  
 END      
 SELECT (  
            CONVERT(  
                VARCHAR,  
                (  
                    SELECT office_id  
                    FROM   Maintenance  
                )  
            ) + '' + CONVERT(VARCHAR, (@MaxID))  
        ) AS MaxID  

END

谁能帮帮我.....

【问题讨论】:

  • 发生死锁。您必须能够使您的代码不那么锁定或简单地处理它们
  • 先生,我已经按照提到的代码制作了一个程序。对于插入,我只是调用那个返回值的过程,我正在插入记录。当有不同用户同时插入数据时发生死锁。如果上述存储过程需要任何更改,我需要帮助。
  • 这段代码有什么意义?如果要生成数字序列,请使用 SEQUENCE。它在所有受支持的 SQL Server 版本中都可用
  • 您也可以使用快照隔离代替排他锁来避免死锁,这是从 2005 年开始提供的。
  • @PanagiotisKanavos 使用快照隔离,他将生成副本,因为快照获取事务开始时的值。所以两个事务可以完美地读取 Col_Counter 的相同值。

标签: sql-server sql-server-2014


【解决方案1】:

正如 Marc 已经回答的那样,请使用 SEQUENCE。它适用于所有受支持的 SQL Server 版本,即 2012 及更高版本。避免它的唯一原因是针对不受支持的版本,例如 2008。

在这种情况下,您可以在更新计数器值的同一语句中设置计数器变量。这样,您就不需要任何事务或锁,例如:

declare @counterValue bigint

UPDATE Maintenance_Counter  
SET    Col_Counter = Col_Counter + 1 , @counterValue=Col_Counter+1
WHERE  COL_NAME        = @Col_Name 

select @counterValue

【讨论】:

  • 我希望在“这样你不需要任何事务或锁”上有所不同。此语句将创建一个更新锁以查找要实际更新的行,然后它将转换为排他锁实际更新值。它将有助于无需锁定以再次读取变量的值,但它仍会在更新期间锁定。这是一篇关于这个主题的好文章。 blogs.extremeexperts.com/?p=2086
【解决方案2】:

你可以使用序列来生成增量值,避免任何阻塞。

我已经修改了我自己的计数器生成器,以直接替代您的。它动态创建 SQL 语句来管理序列,如果我们正在寻找的值的序列不存在,它会创建它。

ALTER PROCEDURE [dbo].[Web_GetMaxColumnID]
    @Col_Name nvarchar(50)
AS
  declare @Value bigint;
  declare @SQL nvarchar(64);
BEGIN
  if not exists(select * from sys.objects where object_id = object_id(N'dbo.MY_SEQUENCES_' + @Col_Name) and type = 'SO') 
  begin
    set @SQL = N'create sequence dbo.MY_SEQUENCES_' + @Col_Name + ' as bigint start with 1';
    exec (@SQL);     
  end

  set @SQL = N'set @Value = next value for dbo.MY_SEQUENCES_' + @Col_Name;      
  exec sp_executesql @SQL, N'@Value bigint out', @Value = @Value out;   

  select @Value ;
END

唯一的不便是您的值可能会在其中出现间隙(因为您可能已经检索了一个值但最终没有使用它)。这在我的桌子上不是问题,但你必须考虑它。

【讨论】:

  • 谢谢先生。但不幸的是,在我的情况下,我无法使用您的程序。如果你能帮助我,将不胜感激
  • 为什么不能用我的存储过程替换?更改名称和参数,您的应用程序不会注意到差异。无法防止锁定原始过程,因为您需要锁定 Maintenance_Counter 以免获得重复值。
  • 当我们运行您的查询时,显示错误。消息 102,级别 15,状态 1,过程 Web_GetMaxColumnID,第 24 行“抛出”附近的语法不正确。
  • 我已经简化了,请重试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多