如何执行行锁定?

10

我想锁定一条记录,并防止其他人对其进行更改。当我释放锁定时,其他人才可以更改该记录。

同时,当记录被锁定时,我想向用户显示一个警告,说明此记录已被锁定,不允许进行更改。

我该如何实现这个功能?

我尝试了所有的隔离级别,但是它们都不满足我的需求。有些隔离级别会等待锁定被释放,然后再进行更改。但我不希望这样做,因为在记录被锁定的时候不允许更新。

我该怎么做才能锁定记录并拒绝所有更改?

我的数据库使用的是SQL Server 2008。


这取决于数据库。哪一个? - pascal
4个回答

11

假设这是MS SQL Server,您可能想要使用UPDLOCK,可能与ROWLOCK表提示)结合使用。我很难找到一个描述理论的好文章,但这里是一个快速的例子:

SELECT id From mytable WITH (ROWLOCK, UPDLOCK) WHERE id = 1 

这个语句会在事务期间对该行添加更新锁(因此需要注意事务何时结束)。由于更新锁与独占锁不兼容(用于更新记录),这将防止任何人在事务结束之前更新此记录。
请注意,其他尝试修改此记录的进程将被阻止,直到事务完成,然后将继续进行他们请求的任何写操作(除非它们超时或被视为死锁进程而被终止)。如果您希望避免这种情况,则其他进程需要使用额外的提示,以便在检测到不兼容锁时中止,或者跳过已更改的记录。
此外,您不应该使用此方法来锁定记录,同时等待用户输入。如果您的意图是这样做,那么您应该在表中添加某种“正在修改”的列。

SQL服务器的锁定机制只适用于保持数据完整性/防止死锁 - 事务应尽可能保持尽可能短,并且绝不能在等待用户输入时进行维护。


当我使用这个更新语句时,它会等待锁被释放后才执行更新操作。我不希望出现这种行为。一旦记录被阻塞,用户必须立即看到一条消息。按照这种方式,我无法通知用户,因为更新语句会等待锁被释放。我希望更新语句停止并以某种方式产生错误/异常/警告,以便我可以通知用户。 - Martijn
@Martijn - 你的第二个更新语句需要使用NOWAIT提示执行,这将导致它在遇到锁定时立即返回,而不是等待超时。 - Justin
这个语句应该是 SELECT id From mytable WITH (ROWLOCK, UPDLOCK) WHERE id = 1,对吗? - nash

1

Sql Server有锁提示,但这些仅限于查询范围。

如果在应用程序中决定锁定记录,则可以使用与乐观锁定相同的机制,并拒绝应用程序对记录进行任何更改。

使用时间戳或GUID作为记录上的锁,并在给出错误的锁定密钥时拒绝访问或更改记录。请注意解锁记录,否则您将获得孤儿记录。


1
我曾经考虑过这种方法,但是如果应用程序崩溃了怎么办?或者服务器宕机了怎么办?在这种情况下,我该如何解锁记录? - Martijn
我认为没有简单的方法。通过记录锁定时间并使用触发器或定时作业清理存在太长时间的锁定来使锁失效。你所做的本质上是悲观锁定。也可以参考这个链接:https://dev59.com/k0bRa4cB1Zd3GeqPwxBv - stombeur

0

在 Stack Overflow 上查看此 重复问题

基本上就是:

begin tran

select * from [table] with(holdlock,rowlock) where id = @id

--Here goes your stuff

commit tran

存档

可能是这样的吗?

update t 
set t.IsLocked = 1 
from [table] t 
where t.id = @id

在更新触发器的某个地方:

if exists (
select top 1 1 
from deleted d 
join inserted i on i.id = d.id
where d.IsLocked = 1 and i.RowVersion <> d.RowVersion)
begin
print 'Row is locked'
rollback tran
end

我已经尝试过这个方法,但我仍然能够选择数据,并且当我执行更新时,语句会等待锁定被释放,然后才执行更新。这不是我想要的。我希望在记录被锁定时通知用户。 - Martijn

0

您不想等待锁释放并在遇到锁时立即显示消息,如果是这种情况,那么您尝试过NOWAIT吗?有关详细信息,请参见表提示(Transact-SQL)SQL Server 2008表提示。要使用NOWAIT的好处,您需要在编辑时锁定记录,请搜索以获取更多详细信息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接