【发布时间】:2018-01-19 08:26:38
【问题描述】:
我正在尝试锁定数据库中的一行,使用标识锁定持有者的 guid 和指定锁定有效时间的到期日期(以防进程崩溃,并且无法释放锁)。到目前为止,这是我想出的:
UPDATE Foo
SET
Lock = @Lock,
LockExpiry = DATEADD(MILLISECOND, @Duration, GETDATE())
WHERE Id = (
SELECT TOP 1 Id
FROM Foo
WHERE (Lock IS NULL OR LockExpiry < GETDATE())
ORDER BY LockExpiry
);
SELECT TOP 1
FROM Foo
WHERE (Lock = @Lock AND LockExpiry > GETDATE())
但是,我在项目中添加了一些日志记录,似乎锁定有时会被其他进程覆盖。我怀疑这里可能存在竞争条件:
- 线程 A 选择顶部等待更新的 Foo,并获取 Foo #3
- 线程 B 选择顶部等待更新的 Foo,并获取 Foo #3
- 线程 B 将 Foo #3 的锁定 ID 设置为某个 guid 'xxxx'
- 线程 B 选择它持有锁的顶部 Foo,并返回 Foo #3
- 线程 A 将 Foo #3 的锁定 ID 设置为某个 guid 'yyyy'
- 线程 A 选择它持有锁的顶部 Foo,并返回 Foo #3
我如何判断这是否正在发生?如果可能,我该如何预防?
编辑
另外,有没有更简单的方法呢?我需要锁定该行,并让锁定在多个事务中持续存在,甚至可能是多个连接。
【问题讨论】:
-
墨菲定律:如果某件事可能失败,它就会失败。如果存在锁定机制是有原因的,因为您无法中继一行中的数据以确保锁定。
-
@Gusman 我正在使用 EntityFramework 的
ExecuteSqlCommandAsync,它会自动将语句包装在BEGIN TRANSACTION和END TRANSACTION中。我可以在事务中调用SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;吗? -
从这里:msdn.microsoft.com/en-us/library/dn220055(v=vs.113).aspx“如果没有现有的本地事务,将使用新事务来执行命令”所以,您可以先创建事务,然后执行查询。
-
好的,现在就测试一下。如果可行,我将投票关闭重复。
标签: sql-server multithreading locking