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

时间:2012-02-29 15:51:44

标签: sql-server database 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;

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

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

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

4 个答案:

答案 0 :(得分:14)

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

FROM TABLE_ITEM with (updlock, holdlock)

答案 1 :(得分:7)

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

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

    select * from ...

    update ...

COMMIT TRANSACTION;

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

  

SERIALIZABLE指定以下内容:

     

语句无法读取已修改但尚未修改的数据   由其他交易承诺。

     

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

     

其他事务无法插入具有键值的新行   落在当前任何语句读取的键范围内   交易直到当前交易完成。

答案 2 :(得分:4)

正如documentation所说:

  

XLOCK

     

指定要占用独占锁,直到   交易完成。如果使用ROWLOCK,PAGLOCK或TABLOCK指定,   独占锁适用于适当的粒度级别。

所以解决方案是使用 <?php // the query $wpb_all_query = new WP_Query(array('post_type'=>'post', 'post_status'=>'publish', 'posts_per_page'=> 10, // As per the post below you need to have a set number of posts. 'offset' => 1 )); ?> <?php if ( $wpb_all_query->have_posts() ) : ?>

WITH(XLOCK, ROWLOCK)

答案 3 :(得分:1)

您是否尝试过 WITH(ROWLOCK)

BEGIN TRAN

   UPDATE your_table WITH (ROWLOCK)
   SET your_field = a_value
   WHERE <a predicate>

COMMIT TRAN