c# - 如何为读-操作-写操作设置锁?

标签 c# sql-server-2008

考虑下表

Key(KeyId int, Sequence varchar(14))

序列值是一个自定义的自动递增键,结合了特定客户对其系统所需的字母和数字。

我们创建了一个名为 GetNextSequence() 的函数,它应该返回序列的下一个值。读取和更新序列的步骤如下

  1. 使用 KeyId 读取序列值:SELECT Sequence FROM [Key] WHERE KeyId = @Id
  2. 解析序列值并确定下一个值
  3. 将序列值写入表:UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id

这是 C# 代码(为清楚起见进行了简化):

var transaction = connection.BeginTransaction(IsolationLevel.RepeatableRead);
var currentSequenceValue = SqlUtils.ExecuteScalar(connection, transaction, "SELECT Sequence FROM [Key] WHERE KeyId = @Id", new SqlParameter("@Id", keyId));
var updatedSequenceValue = ParseSequence(currentSequenceValue);
SqlUtils.ExecuteScalar(connection, transaction, "UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id", new SqlParameter("@Id", keyId), new SqlParameter("@Sequence", updatedSequenceValue));
transaction.Commit();
return updatedSequenceValue;

我们的问题在于两个不同的服务器可以访问相同的序列,我们最终陷入僵局

事务(进程 ID X)与另一个进程在锁定资源上发生死锁,并已被选为死锁牺牲品。重新运行事务。

在 C# 中,我尝试设置不同的锁组合,如事务隔离 IsolationLevel.RepeatableReadIsolationLevel.Serializable 或在 SQL 中使用表提示 ROWLOCKHOLDLOCK,但没有成功。

我希望每个服务器都能够以原子方式读取、操作和更新序列。在这种情况下设置锁的正确方法是什么?

最佳答案

问题是为读取获取的默认锁不能避免竞争条件,因为可以在同一条记录上获取多个读取锁。

情况是,进程 A 在 X 行上获取读锁。然后进程 B 在 A 在其“客户端”(在服务器程序中)工作时获取读锁。然后 A 请求升级写锁,而 B 在客户端工作,此时它被告知等待 B 的读锁被释放。然后 B 请求写锁并等待 A 释放它的读锁。两者现在都在等待对方,以便他们可以获得更独占的写入锁。

解决方案是独占锁;您可以使用 XLOCK 提示指定它。独占锁基本上是为读取获取的写入级锁,并且在这种情况下使用,您希望在这种情况下写入您正在阅读的内容。如评论中所述,仅当语句在显式事务范围内执行时才会维护独占锁,因此请确保在读取值的“工作单元”期间设置独占锁,确定如何推进它,然后更新它。

我会在行级别 (ROWLOCK) 使用它,除非您同时更新大量相似的序列;如果每个事务只处理一行,则获取页级或表级排他锁会使每个人都等待不需要的数据。

关于c# - 如何为读-操作-写操作设置锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4728217/

相关文章:

sql - 使用来自其他表的动态值循环更新 SQL 表

c# - 仅在其他机器上的 MySQL 语法错误

c# - 使用 MVVM C#​​ 将 UpdateSourceTrigger 设置为 WPF ListBox 项源控件的显式

c# - 使用 C# 选择、复制和重新插入一系列 Excel 行

sql-server - SQL Server GUID 排序算法。为什么?

SQL Server 如何检索两个数字之间的所有数字

c# - ASP.Net - 从 RepeaterItem 获取数据

c# - 在不使用应用程序的 Dispose() 方法的情况下关闭/处置应用程序时调用 FACTORY 类中的方法

sql-server - 获取查询返回的总行数

sql-server - 如何从 SQL Server 存储过程返回值并在 Access VBA 中使用它们