如何让SQL Server事务使用记录级锁?

时间:2010-04-15 15:00:22

标签: sql-server transactions nonblocking record-locking table-locking

我们有一个应用程序最初是作为桌面应用程序编写的,很多年前。无论何时打开编辑屏幕,它都会启动一个事务,如果单击“确定”,则会提交,或者如果单击“取消”,则会回滚。这适用于桌面应用程序,但现在我们正在尝试迁移到ADO.NET和SQL Server,并且长时间运行的事务存在问题。

我发现当多个用户同时尝试编辑同一个表(不同的子集)时,我们会遇到问题。在我们的旧数据库中,每个用户的事务将获取他们在事务期间修改的每个记录的记录级锁定;由于不同的用户正在编辑不同的记录,每个人都有自己的锁,一切正常。但是在SQL Server中,只要有一个用户在事务中编辑记录,SQL Server就会在整个表上锁定。当第二个用户尝试编辑同一个表中的不同记录时,第二个用户的应用程序只是锁定,因为SqlConnection会阻塞,直到第一个用户提交或回滚。

我知道长时间运行的事务很糟糕,而且我知道最佳解决方案是更改这些屏幕,以便它们不再使事务保持打开状态很长时间。但是,由于这意味着一些侵入性和风险性的变化,我还想研究是否有办法让这些代码按原样运行,我知道我的选择是什么。

如何在SQL Server中获取两个不同用户的事务来锁定单个记录而不是整个表?

这是一个快速而肮脏的控制台应用程序,用于说明问题。我创建了一个名为“test1”的数据库,其中一个名为“Values”的表只有ID(int)和Value(nvarchar)列。如果您运行该应用程序,它会要求修改ID,启动事务,修改该记录,然后保持事务处于打开状态,直到您按Enter键。我希望能够

  1. 启动程序并告诉它更新ID 1;
  2. 让它获得交易并修改记录;
  3. 启动程序的第二个副本并告诉它更新ID 2;
  4. 能够在第一个应用程序的事务仍处于打开状态时更新(并提交)。
  5. 目前它在第4步冻结,直到我回到应用程序的第一个副本并关闭它或按ENTER键以便提交。对command.ExecuteNonQuery的调用将阻塞,直到第一个连接关闭。

    public static void Main()
    {
        Console.Write("ID to update: ");
        var id = int.Parse(Console.ReadLine());
        Console.WriteLine("Starting transaction");
        using (var scope = new TransactionScope())
        using (var connection = new SqlConnection(@"Data Source=localhost\sqlexpress;Initial Catalog=test1;Integrated Security=True"))
        {
            connection.Open();
            var command = connection.CreateCommand();
            command.CommandText = "UPDATE [Values] SET Value = 'Value' WHERE ID = " + id;
            Console.WriteLine("Updating record");
            command.ExecuteNonQuery();
            Console.Write("Press ENTER to end transaction: ");
            Console.ReadLine();
            scope.Complete();
        }
    }
    

    以下是我已尝试过的一些事情,行为没有变化:

    • 将事务隔离级别更改为“read uncommitted”
    • 在UPDATE语句中指定“WITH(ROWLOCK)”

3 个答案:

答案 0 :(得分:5)

只是检查,但ID列上是否有主键或唯一索引?

答案 1 :(得分:1)

关注乐观与悲观的锁定。

编辑: 上一篇文章链接到经典ado ...抱歉。

http://msdn.microsoft.com/en-us/library/cs6hb8k4(VS.71).aspx

答案 2 :(得分:1)

可能是在行锁设置为“关闭”的情况下创建了索引 在这种情况下,查询中的“WITH(ROWLOCK)”将不起作用。

您可以使用ALTER INDEX将其重新打开,例如:

ALTER INDEX [PK_Values] ON [Values] SET (ALLOW_ROW_LOCKS = ON)