【问题标题】:How do I get the right locks for this SQL?如何为这个 SQL 获得正确的锁?
【发布时间】:2011-12-01 20:11:40
【问题描述】:

我的数据库是 SQL Server 2005/8。在预订系统中,我们对一个活动的预订限制为 24 个。存储过程中的此代码检查: - 当前用户 (@UserId) 尚未预订活动 (@EventsID) - 当前活动的当前预订名单低于 24 - 插入一个新的预订。

BEGIN TRANSACTION 
IF (((select count (*) from dbo.aspnet_UsersEvents with (updlock) 
      where UserId = @UserId and EventsId = @EventsId) = 0) 
AND  ((SELECT Count(*)  FROM dbo.aspnet_UsersEvents with (updlock) 
      WHERE EventsId = @EventsId) < 24))
BEGIN
  insert into dbo.aspnet_UsersEvents (UserId, EventsId) 
      Values (@UserId, @EventsId)
END
COMMIT

问题是它不安全。两个用户可能同时执行测试并得出结论他们都可以预订。两者都插入一行,我们最终得到 25 个预订。

简单地将其包含在事务中是行不通的。我尝试将 WITH (UPDLOCK) 添加到选择中,希望其中一个会获取更新锁并将另一个锁在外面。那是行不通的。

【问题讨论】:

    标签: sql-server data-integrity locks


    【解决方案1】:

    三个选项:

    1. SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
    2. 将锁定提示更改为WITH (UPDLOCK, HOLDLOCK)
    3. 为 dbo.aspnet_UsersEvents 添加一个唯一约束,并在插入周围添加一个 TRY/CATCH

    如果省略HOLDLOCK,您可以使用以下脚本确认锁定已被占用并立即释放。当使用HOLDLOCK 时,您还将看到锁没有被释放(没有'释放 KEY 上的锁引用'输出)。

    (Gist script)

    【讨论】:

    • 为什么这么复杂?看我的回答。
    • 因为您的答案在事务上不安全。一个单一的陈述总是一致的是一个常见的谬误。
    • 能否请您指出一些有关SQL Server 不保证单个语句不一致的文档。我假设他没有在READ UNCOMMITTED 运行。
    • 添加 HOLDLOCK 提示似乎是我缺少的主要部分 - 谢谢。
    • 我尝试了 REPEATABLE READ,但实际上还不够。即使读取获得与以前相同的行(可重复),它们也会错过新的行。使用 SERIALIZABLE 确实有效,但我只是有点害怕 SERIALIZABLE。
    【解决方案2】:

    只需在READ COMMITTED 或更高级别的声明中完成。

    INSERT dbo.aspnet_UsersEvents
           (UserId,EventsId)
    OUTPUT inserted.UserEventsId -- Or whatever, just getting back one row identifies the insert was successful
    SELECT @UserId
           , @EventsId
     WHERE ( SELECT COUNT (*)
               FROM dbo.aspnet_UsersEvents
              WHERE UserId = @UserId
                    AND EventsId = @EventsId ) = 0
           AND ( SELECT COUNT(*)
                   FROM dbo.aspnet_UsersEvents
                  WHERE EventsId = @EventsId ) < 24 
    

    旁注:您的SELECT COUNT(*) 用于重复检查似乎过多,我个人会使用NOT EXISTS(SELECT NULL FROM ... WHERE UserID = ..., EventsID = ...

    【讨论】:

    • 您提出了一个有用的观点,即 Exists 测试比计数更清晰。谢谢。
    猜你喜欢
    • 2015-04-08
    • 2021-12-25
    • 2014-03-05
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-25
    相关资源
    最近更新 更多