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 子句中未使用它。
没有直接的方法可以实现您想要的操作:在读取未提交隔离时读取行,仅对与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语义时,您的阻塞机会可能会大大增加。
| 归档时间: |
|
| 查看次数: |
983 次 |
| 最近记录: |