SQL 死锁 - 同时调用相同的存储过程

Kev*_*vin 3 sql-server deadlock stored-procedures update

我正在使用一个相对复杂的系统,其中根据需要从多个数据源更新静态表,因为动态加载数据需要 4-5 秒,我们更喜欢快速向用户显示结果。

过程:

  1. 用户或应用程序打开特定订单
  2. 调用存储过程来检索数据(我们称之为 dbo.Get_Data)
  3. dbo.Get_Data 检查事件日志以查看自上次查找数据以来是否有任何更改
  4. 如果它发现一个新事件,它会运行昂贵的查询来更新数据
  5. 数据返回
IF (    SELECT [LastStaticUpdateEvent] < [LastSaveEvent] 
        FROM [dbo].[Events] 
        WHERE [OrderNumber] = @OrderNumber
    )
    BEGIN
       -- Update Static table
       UPDATE [Static]
       SET [Static].[A] = [App].[A]
          ,[Static].[B] = [App].[B]
       FROM [dbo].[AppData]            AS [App]   -- View with many joins (4-5 secs)
       INNER JOIN [dbo].[StaticResuts] AS [Static]
           ON [Static].[OrderNumber] = [App].[OrderNumber]
       WHERE [App].[OrderNumber] = @OrderNumber

       -- Update Event Log
       UPDATE [dbo].[Events]
       SET [LastStaticUpdateVent] = SYSDATETIME()
       WHERE [OrderNumber] = @OrderNumber

    END

SELECT * FROM [dbo].[StaticResults]
Run Code Online (Sandbox Code Playgroud)

问题是可以同时请求这些数据。如果用户 A 和用户 B 在第一次 UPDATE 完成之前都调用了 dbo.Get_Data,则会发生死锁。

是否有一种好的方法或模式可以让第二次调用等到第一次调用完成后再继续查找?

Dav*_*oft 6

是否有一种好的方法或模式可以让第二次调用等到第一次调用完成后再继续查找?

拥有一次只能由一个会话运行的代码块的最简单方法是使用Application Lock。您可以使用 SQL Server 的锁定引擎,但不是锁定特定的行、页或表,而是使用自定义名称创建锁定。例如

begin transaction;

--begin exclusive section
exec sp_getapplock @Resource = 'Get_Data Exclusive Lock', @LockMode = 'Exclusive';  

--do stuff in only one session

--end exclusive section
exec sp_releaseapplock @Resource = 'Get_Data Exclusive Lock';  


commit transaction;
Run Code Online (Sandbox Code Playgroud)