我有很多.NET进程从SQL Server 2008数据库表读取消息并一次处理一个。我实现了一个简单的SP来“锁定”任何一个进程正在读取的行,以避免任何两个进程处理同一行。
BEGIN TRAN
SELECT @status = status FROM t WHERE t.id = @id
IF @status = 'L'
BEGIN
-- Already locked by another process. Skip this
RETURN 0
END
ELSE
UPDATE t SET status = 'L' WHERE id = @id
RETURN 1
END
COMMIT
然而,这是有缺陷的:有时一行被“锁定”并处理两次。我怀疑存在并发问题:两个进程在更新之前读取状态。
我认为这可以通过以某种方式实现读取块来解决(即使事务块读取),但我不确定如何执行此操作。有人可以帮忙吗?
提前多多感谢
赖安
答案 0 :(得分:3)
为什么不用
替换整个事物UPDATE t SET status = 'L' WHERE id = @id and status <> 'L'
RETURN @@ROWCOUNT
这将避免2次表访问并保持锁定打开超过必要的时间。
答案 1 :(得分:2)
你尝试过使用:
SELECT @status = status FROM t (UPDLOCK) WHERE t.id = @id
有关详细信息,请参阅this链接。
您缺少的是SELECT
语句通常不会锁定该行,因此如果一个进程执行了SELECT
但尚未执行UPDATE
,然后另一个进程出现并执行SELECT,然后它将返回该行,因为你还没有锁定它。
通过使用UPDLOCK
,您可以使用SELECT语句锁定该行,并阻止其他进程将其恢复,直到第一个进程提交事务,这是您想要的行为。
修改强> 当然,像马丁所说的那样用一条陈述来做这件事是最简单的方法,你可以避免必须处理锁定问题。