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

Joe*_*ite 3 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. 让它能够在第一个应用程序的事务仍处于打开状态时更新(并提交).

目前它在第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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 将事务隔离级别更改为"未提交读取"
  • 在UPDATE语句中指定"WITH(ROWLOCK)"

Nei*_*oss 5

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