简体   繁体   English

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

[英]How can I get SQL Server transactions to use record-level locks?

We have an application that was originally written as a desktop app, lo these many years ago. 我们的应用程序最初是作为桌面应用程序编写的,多年前就是这样。 It starts a transaction whenever you open an edit screen, and commits if you click OK, or rolls back if you click Cancel. 无论何时打开编辑屏幕,它都会启动一个事务,如果单击“确定”,则会提交,或者如果单击“取消”,则会回滚。 This worked okay for a desktop app, but now we're trying to move to ADO.NET and SQL Server, and the long-running transactions are problematic. 这适用于桌面应用程序,但现在我们正在尝试迁移到ADO.NET和SQL Server,并且长时间运行的事务是有问题的。

I found that we'll have a problem when multiple users are all trying to edit (different subsets of) the same table at the same time. 我发现当多个用户同时尝试编辑同一个表(不同的子集)时,我们会遇到问题。 In our old database, each user's transaction would acquire record-level locks to every record they modified during their transaction; 在我们的旧数据库中,每个用户的事务将获取他们在事务期间修改的每个记录的记录级锁定; since different users were editing different records, everyone gets their own locks and everything works. 由于不同的用户正在编辑不同的记录,每个人都有自己的锁,一切正常。 But in SQL Server, as soon as one user edits a record inside a transaction, SQL Server appears to get a lock on the entire table. 但是在SQL Server中,只要有一个用户在事务中编辑记录,SQL Server就会在整个表上获得锁定 When a second user tries to edit a different record in the same table, the second user's app simply locks up, because the SqlConnection blocks until the first user either commits or rolls back. 当第二个用户尝试编辑同一个表中的不同记录时,第二个用户的应用程序只会锁定,因为SqlConnection会阻塞,直到第一个用户提交或回滚。

I'm aware that long-running transactions are bad, and I know that the best solution would be to change these screens so that they no longer keep transactions open for a long time. 我知道长时间运行的事务很糟糕,我知道最好的解决方案是更改这些屏幕,以便它们不再能够长时间保持事务处理。 But since that would mean some invasive and risky changes, I also want to research whether there's a way to get this code up and running as-is, just so I know what my options are. 但是,由于这将意味着一些侵入性和风险的变化,我还想研究是否有一种方法来使这些代码按原样运行,因此我知道我的选择是什么。

How can I get two different users' transactions in SQL Server to lock individual records instead of the entire table? 如何在SQL Server中获取两个不同用户的事务来锁定单个记录而不是整个表?

Here's a quick-and-dirty console app that illustrates the issue. 这是一个快速而肮脏的控制台应用程序,用于说明问题。 I've created a database called "test1", with one table called "Values" that just has ID (int) and Value (nvarchar) columns. 我创建了一个名为“test1”的数据库,其中一个名为“Values”的表只有ID(int)和Value(nvarchar)列。 If you run the app, it asks for an ID to modify, starts a transaction, modifies that record, and then leaves the transaction open until you press ENTER. 如果您运行该应用程序,它会要求修改ID,启动事务,修改该记录,然后保持事务处于打开状态,直到您按Enter键。 I want to be able to 我希望能够

  1. start the program and tell it to update ID 1; 启动程序并告诉它更新ID 1;
  2. let it get its transaction and modify the record; 让它得到它的交易并修改记录;
  3. start a second copy of the program and tell it to update ID 2; 启动程序的第二个副本并告诉它更新ID 2;
  4. have it able to update (and commit) while the first app's transaction is still open. 让它能够在第一个应用程序的事务仍处于打开状态时更新(并提交)。

Currently it freezes at step 4, until I go back to the first copy of the app and close it or press ENTER so it commits. 目前它在第4步冻结,直到我回到应用程序的第一个副本并关闭它或按ENTER键以便提交。 The call to command.ExecuteNonQuery blocks until the first connection is closed. 对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();
    }
}

Here are some things I've already tried, with no change in behavior: 以下是我已经尝试过的一些事情,没有改变行为:

  • Changing the transaction isolation level to "read uncommitted" 将事务隔离级别更改为“未提交读取”
  • Specifying a "WITH (ROWLOCK)" on the UPDATE statement 在UPDATE语句中指定“WITH(ROWLOCK)”

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

Look into optimistic versus pessimistic locking. 研究乐观与悲观的锁定。

Edit: Previous article linked to classic ado...sorry. 编辑:上一篇文章链接到经典的ado ...抱歉。

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

Probably the index was created with row locks set to "off". 可能是在行锁设置为“关闭”的情况下创建索引。
"WITH (ROWLOCK)" in a query would have no effect in that case. 在这种情况下,查询中的“WITH(ROWLOCK)”将不起作用。

You can turn them back on with ALTER INDEX , eg: 您可以使用ALTER INDEX重新打开它们,例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM