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

Pie*_*ant 5 c# sql-server-2008

考虑下表

键(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;
Run Code Online (Sandbox Code Playgroud)

我们的问题在于两个不同的服务器可以访问相同的序列,最终导致死锁

事务(进程ID X)在锁资源上与另一个进程死锁,并被选为死锁牺牲品.重新运行该交易.

在C#中,我试图建立不同的锁组合就像一个事务隔离IsolationLevel.RepeatableReadIsolationLevel.Serializable使用表提示或SQL ROWLOCKHOLDLOCK,但没有成功.

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

gbn*_*gbn 1

我建议在事务期间使用独占行级锁(ROWLOCK、XLOCK、HOLDLOCK)。到目前为止,您对提示等的使用还不够。

BEGIN TRAN
    SELECT Sequence FROM [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE KeyId = @Id

    Parse the sequence value and determine the next value

    UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id
COMMIT
Run Code Online (Sandbox Code Playgroud)

尽管如此,我至少会考虑将范围缩小到单个事务

    UPDATE [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK)
    SET Sequence = dbo.scalarudf(...)
    WHERE KeyId = @Id
Run Code Online (Sandbox Code Playgroud)

编辑:如果使用 SERIALIZABLE,则不需要 HOLDLOCK。由于范围的锁定方式,“RepeatableRead”可能还不够