我们有一个应用程序最初是作为桌面应用程序编写的,很多年前。每当您打开编辑屏幕时,它都会启动一个事务,如果单击“确定”则提交,如果单击“取消”则回滚。这对于桌面应用程序来说效果很好,但现在我们正在尝试迁移到 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();
    }
}

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

  • 将事务隔离级别更改为“读未提交”
  • 在 UPDATE 语句上指定“WITH (ROWLOCK)”
有帮助吗?

解决方案

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

其他提示

研究乐观锁定与悲观锁定。

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

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

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

您可以使用以下命令重新打开它们 改变索引, ,例如:

ALTER INDEX [PK_Values] ON [Values] SET (ALLOW_ROW_LOCKS = ON)
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top