【问题标题】:Insert after failed update, transaction is not locking更新失败后插入,事务未锁定
【发布时间】:2011-12-29 15:17:49
【问题描述】:

我有一个包含用户设置(如窗口位置)的小表,只是一个简单的名称=值对表,其中名称需要是唯一的:

CREATE TABLE dbo.[Settings]
(
  [Name] [nvarchar](100) NOT NULL,
  [Value] [nvarchar](4000) NOT NULL,
  CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED
  (
    [Name] ASC
  ) ON [PRIMARY]
) ON [PRIMARY]

当我存储更改的设置时,我开始一个事务,尝试更新值,如果更新失败(因为记录还不存在)我插入一条新记录:

// Begin Transaction

cmd.CommandText = "UPDATE dbo.[Settings] SET [Value] = @value WHERE [Name] = @name";
cmd.Parameters.AddWithValue("@value", value);
cmd.Parameters.AddWithValue("@name", name);
int cmdrc = cmd.ExecuteNonQuery();
if (cmdrc != 1)
{
  cmd.CommandText = "INSERT INTO dbo.[Settings] ([Name], [Value]) VALUES (@name, @value)";
  cmd.ExecuteNonQuery();
}

// Commit Transaction

我的问题:如果两个线程碰巧同时尝试存储同名的值(这种情况很少发生,但仍然如此),就会发生冲突:

两个线程都尝试更新,两个线程都得到cmdrc==0,两个线程都尝试插入新值,只有一个会成功,另一个会得到一个SqlException。

我以为事务会锁定记录,但似乎由于在Update语句上没有找到记录,它没有被锁定,所以尝试更新相同'name'值的其他线程不会被阻塞, 竞争条件发生。

有没有办法让 SQL Server 锁定关键字,即使没有找到它的记录?

【问题讨论】:

    标签: sql-server transactions locking


    【解决方案1】:

    你可以使用

    UPDATE dbo.[Settings] 
    WITH (HOLDLOCK, UPDLOCK)
    SET [Value] = @value WHERE [Name] = @name
    

    所以锁是持有的,但为什么要麻烦呢?为什么不直接忽略该错误,因为无论如何哪个会“获胜”是任意的。

    【讨论】:

    • 老实说,我问主要是出于好奇。谢谢指点!
    • 啊,是的,他的问题很相似(但他似乎期望插入比更新更多,除了我)。有趣的信息,谢谢!
    • @Sam - 顺便说一句,我没有提到这一点,因为它在前面的链接中有所介绍,但如果你在 2008 年,你可以在一个声明中使用 MERGE 来做到这一点。 Though you still need locking hints.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-12
    • 2019-07-11
    相关资源
    最近更新 更多