c# - 如何使表用作计数器的行锁?
问题描述
我有一个 API,它使用不同的提供者进行交易,对于每个提供者,我在表中处理一个内部计数器。问题来了,因为我们将收到更多的流量,并且我们有一个重复编号的交易。
我用于计数器的查询是下一个:
public async Task<int> GetNumeration(int id)
{
var providerNumerationDb = await _dbContextEf
.providerNumeration
.SingleOrDefaultAsync(x => x.providerId == id);
if (providerNumerationDb == null)
{
providerNumerationDb = new ProviderNumeration
{
providerId = id,
number = 1,
};
await _dbContextEf.AddAsync(providerNumerationDb);
}
else
{
providerNumerationDb.Number += 1;
}
await _dbContextEf.SaveChangesAsync(0);
_dbContextEf.Entry(providerNumerationDb).State = EntityState.Detached;
return providerNumerationDb.Number;
}
我想知道在没有性能影响或其他解决方案的情况下制作 Rowlock 的最佳策略。如果您认为使用 StoredProcedure 或其他方式的解决方案更好,它也是有效的,我们可以更改实现。
解决方案
下面是使用SERIALIZABLE
事务的示例。为更新获取更新键上的行锁,当 ID 不存在时,为插入获取键范围锁。后者将限制范围内其他插入的并发性,但可能不会成为问题,因为事务将立即提交,释放锁。
CREATE TABLE dbo.Provider(
ProviderId int NOT NULL CONSTRAINT PK_Provider PRIMARY KEY
, Number int NOT NULL
);
GO
CREATE PROCEDURE dbo.GetNextProviderNumber
@ProviderID int
AS
SET NOCOUNT, XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
DECLARE @Number int;
BEGIN TRY
BEGIN TRAN;
UPDATE dbo.Provider
SET @Number = Number += 1
WHERE ProviderID = @ProviderID;
IF @@ROWCOUNT = 0
BEGIN
SET @Number = 1;
INSERT INTO dbo.Provider(ProviderID, Number)
VALUES(@ProviderID, @Number);
END
COMMIT;
SELECT @Number AS Number;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
GO
推荐阅读
- angular - 在angular6中将excel模板文件下载到浏览器
- assembly - 装配过程中的顺序
- python - 是否可以删除python中字节的最后一个字节?
- python - 由另一列中的数值引导的列中的完整字符串值
- python - 将python浮点数转换为字节
- laravel - 删除请求重定向到未找到
- ios - 如何在 iOS Objective-C 中集成“使用 Apple 登录”流程?
- javascript - 如何使用 c3js 显示条形图的标签?
- java - Java流在reduce操作的情况下给出语法错误
- serialization - Json.NET - 有条件地序列化