不返回任何记录的查询挂在锁上

CSh*_*pie 5 sql-server locking blocking

我们注意到这种奇怪的情况,即不返回任何记录的查询将在WITH(UPDLOCK)指定时等待锁定。

查询很简单

SELECT primaryKey FROM someTable WITH(UPDLOCK) 
WHERE columA = 'A' 
      AND columnB BETWEEN 6 AND 8 
      AND columnC != 'C' ;
Run Code Online (Sandbox Code Playgroud)

运行此查询的系统使用隔离级别read uncommissed,我们无法更改该级别。

有没有一种方法,例如提示,不会让这个等待,但如果它会产生结果,仍然锁定记录?

我注意到索引的使用在其中发挥了作用,因为其他查询没有这种行为。但遗憾的是,定义索引也不可能。

我想出的唯一解决方案是IF EXISTS在执行实际查询之前在其中抛出一个,但由于竞争条件,它仍然可能发生。

类似的问题已在这里得到解答:Why does UPDLOCK Causes SELECTs tohang (lock)?

如果没有索引来定位要锁定的行,则所有测试行都将被锁定,并且合格行上的锁将被保留,直到事务完成。

但我不认为那里提供的答案适用于我的问题版本,因为查询不会返回“合格”的行。

遗憾的是我无法访问该系统。我们只能对某些表运行查询。

目的是锁定并读取记录(如果存在)并最终更新它。如果缺失则插入。

UPDLOCK使用它是因为根据我的理解,当锁定读未提交隔离时,这是正确的。

该表有主键,但在 where 子句中未使用它。

Pau*_*ite 7

没有直接的方法可以实现您想要的操作:在读取未提交隔离时读取行,仅对与U给定谓词匹配的行进行锁定。

如何UPDLOCK运作

当您指定 时UPDLOCK,SQL Server 将U在行或页级别获取锁定。如果需要表锁,SQL Server 会X改为获取锁。

无论是否找到任何匹配的行,表锁和页锁都会保留到事务结束。当使用行级锁时,SQL Server 总是先对行进行锁定,然后U再测试它是否符合条件。

在测试行并发现与同一数据访问运算符(例如扫描或查找)中的谓词不匹配的特殊情况下,当前行上的锁将在获取下一行上的锁U之前释放。U否则,U锁将像往常一样保持到事务结束。

这是 SQL Server 如何实现提示的描述UPDLOCK我确实理解文档中不清楚,或者您希望它如何表现。尽管如此,事情就是这样。

解决方法

没有完美的方法来实现您想要的,但有一些部分解决方案。

第一个是WITH (UPDLOCK, READPAST, ROWLOCK)

SELECT 
    ST.PrimaryKey
FROM dbo.SomeTable AS ST 
    WITH (UPDLOCK, READPAST, ROWLOCK) 
WHERE
    ST.ColumnA = 'A' 
    AND ST.ColumnB BETWEEN 6 AND 8 
    AND ST.ColumnC != 'C';
Run Code Online (Sandbox Code Playgroud)

如果要测试的行具有不兼容的行级锁,这将跳过阻塞。如果在页或表级别持有不兼容的锁,查询仍会阻塞。


第二种解决方法是在会话隔离级别进行读取,然后U通过重新连接主键来锁定单独的数据访问:

SELECT 
    CA.PrimaryKey 
FROM dbo.SomeTable AS ST -- No hints here
CROSS APPLY 
(
    SELECT TOP (1) 
        ST2.PrimaryKey
    FROM dbo.SomeTable AS ST2 
        WITH (UPDLOCK, FORCESEEK)
    WHERE
        ST2.PrimaryKey = ST.PrimaryKey
) AS CA
WHERE
    ST.ColumnA = 'A' 
    AND ST.ColumnB BETWEEN 6 AND 8 
    AND ST.ColumnC != 'C';
Run Code Online (Sandbox Code Playgroud)

重要的是要完全遵循那里的图案,包括顶部。如果正确实施,该方法在实践中应该非常可靠。

更新插入

我不得不提这个问题似乎是一个 upsert 模式。单独采取UPDLOCK不够的

您还需要围绕更新插入的每个部分进行事务,并提供一个SERIALIZABLE提示以确保任何不存在的行在执行插入之前继续不存在。更新锁只能对存在的行进行。请参阅Michael J Swart 撰写的SQL Server UPSERT 模式和反模式

当然,当您使用SERIALIZABLE语义时,您的阻塞机会可能会大大增加。