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)
在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指定以下内容:
语句无法读取已修改但尚未由其他事务提交的数据.
在当前事务完成之前,没有其他事务可以修改当前事务已读取的数据.
其他事务无法插入新行,其键值将落在当前事务中任何语句读取的键范围内,直到当前事务完成为止.
正如文档所说:
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)