在SQL Server中,如何以类似于Oracle的"SELECT FOR UPDATE WAIT"的方式锁定单行?

Par*_*yde 22 database sql-server oracle concurrency locking

我有一个程序连接到Oracle数据库并对其执行操作.我现在想要调整该程序以支持SQL Server数据库.

在Oracle版本中,我使用"SELECT FOR UPDATE WAIT"来锁定我需要的特定行.我在更新基于SELECT的结果的情况下使用它,而其他会话绝对不能同时修改它,所以他们必须先手动锁定它.系统很可能会同时尝试访问相同的数据.

例如:
两个用户尝试获取具有最高优先级的数据库中的行,将其标记为忙,对其执行操作,并将其标记为可用以供以后使用.在Oracle中,逻辑基本上会像这样:

BEGIN TRANSACTION;
SELECT ITEM_ID FROM TABLE_ITEM WHERE ITEM_PRIORITY > 10 AND ITEM_CATEGORY = 'CT1'
    ITEM_STATUS = 'available' AND ROWNUM = 1 FOR UPDATE WAIT 5;
UPDATE [locked item_id] SET ITEM_STATUS = 'unavailable';
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

请注意,查询是在我的代码中动态构建的.另请注意,当先前最有利的行被标记为不可用时,第二个用户将自动转到下一个,依此类推.此外,处理不同类别的不同用户不必等待彼此的锁被释放.最糟糕的是,5秒后,将返回错误并取消操作.

最后,问题是:如何在SQL Server中实现相同的结果?我一直在寻找锁定提示,理论上看起来它们应该起作用.但是,唯一阻止其他锁的锁是"UPDLOCK"和"XLOCK",它们只能在表级工作.
那些在行级别工作的锁定提示都是共享锁,它们也不能满足我的需求(两个用户可以同时锁定同一行,都将其标记为不可用并对相应的项执行冗余操作).

有些人似乎添加了一个"时间修改"列,因此会话可以验证他们是修改它的人,但这听起来会有很多冗余和不必要的访问.

And*_*mar 16

你可能正在寻找with (updlock, holdlock).这将使select获取exclusive锁定,这是更新所需,而不是shared锁定.该holdlock提示告诉SQL Server保持锁定,直到事务结束.

FROM TABLE_ITEM with (updlock, holdlock)
Run Code Online (Sandbox Code Playgroud)

  • `updlock`改变了锁的类型(独占与共享),但没有锁定的内容.要锁定整个表,指定`with(tablock)`,或者要锁定一行,指定`with(rowlock)` (5认同)

Pau*_*sik 9

在SQL Server中有锁定提示,但它们不会像您提供的Oracle示例那样跨越它们的语句.在SQL Server中执行此操作的方法是在包含要执行的语句的事务上设置隔离级别.请参阅此MSDN页面,但一般结构如下所示:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

    select * from ...

    update ...

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)

SERIALIZABLE是最高的隔离级别.请参阅其他选项的链接.来自MSDN:

SERIALIZABLE指定以下内容:

语句无法读取已修改但尚未由其他事务提交的数据.

在当前事务完成之前,没有其他事务可以修改当前事务已读取的数据.

其他事务无法插入新行,其键值将落在当前事务中任何语句读取的键范围内,直到当前事务完成为止.

  • 很高兴知道,但似乎Serializable只有在数据被修改时才会阻止读取,所以直到那一刻它才会阻止锁定. (5认同)
  • 这会获得共享锁,后来又会获得独占锁,因此很容易出现死锁 (5认同)
  • 好吧,在用它愚弄了一点之后我发现了一个潜在的解决方案,虽然我会承认它感觉便宜并且在某些情况下可能不可靠(SQL Server并不总是尊重行锁).我所做的是将事务隔离级别设置为read committed,然后开始我的事务.在我选择获取最有利的行索引之前,我使用相同的搜索参数进行更新并且不进行任何更新(例如set item_name = item_name).这有效地锁定了我的行,直到事务结束.然后,访问此表的任何其他事务都可以指定readpast或nolock. (4认同)
  • 你一定误解了我之前的评论。我所说/的意思是,虽然其他事务无法修改数据(如您引用的部分的文档中所述),但它们仍然可以获得它的锁。这意味着两个会话可以同时锁定同一行,并且一个会话会等待第一个会话将其项目设置为“不可用”。当第一个会话释放该行时,第二个会话也会更新该行以将其设置为“不可用”。这导致两个会话相信他们拥有该项目的所有权。 (2认同)

Gui*_*cha 9

正如文档所说:

XLOCK

指定在事务完成之前要获取并保持排他锁。如果用ROWLOCK,PAGLOCK或TABLOCK指定,则排他锁适用于适当的粒度级别。

所以解决方案正在使用WITH(XLOCK, ROWLOCK)

BEGIN TRANSACTION;

SELECT ITEM_ID
FROM TABLE_ITEM
WITH(XLOCK, ROWLOCK)
WHERE ITEM_PRIORITY > 10 AND ITEM_CATEGORY = 'CT1' AND ITEM_STATUS = 'available' AND ROWNUM = 1;

UPDATE [locked item_id] SET ITEM_STATUS = 'unavailable';

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)