SQL 服务器 - updlock,可序列化不阻止 100%

SQL Server - updlock, serializable doesn't block 100%

我有一个存储过程,如果 ID 在 table 中不存在,我的代码基本上会插入一条记录,所有这些都包含在从应用程序调用的事务中,该应用程序也使用事务范围。

存储过程代码片段如下所示:

begin transaction

set @exist = (select top 1 id from table with (updlock, serializable)  
              where uid = @uid and gid = @gid)

-- if doesn't exist... insert
commit transaction

对于更多的背景,有多个服务器调用这个确切的存储过程。

我已经阅读了多个资源,它们似乎都指出使用 (updlock, serializable) 应该确保阻塞,类似于此 Why does my SQL Server UPSERT code sometimes not block?

有些地方还提到,如果查询计划不同,锁定的资源可能不同,但这肯定不适用于我的情况,因为它在 table、参数、查询等方面是相同的。

但我注意到 table 中有重复的记录(不是很多),所以我的问题是我对 updlock 的使用以及可能导致此行为的原因的了解是否存在差距?

要确认这一点,您应该 运行 扩展事件会话并捕获调用该过程时获取的实际锁。似乎锁只是在行级别获取并且查看 definition of HOLDLOCK 声明它仅用于语句。

您可能需要包含一个 TABLOCK,或者考虑将事务的 TRANSACTION ISOLATION LEVEL 设置为 SERIALIZABLE。

好吧,这很尴尬,我想删除这个问题,但重点是在仔细检查生产存储过程后,我发现版本与测试环境不同。

它使用的是不带可序列化的 UPDLOCK,这解释了为什么存在重复记录,因为如果该行不存在,则在 select 期间不会锁定。