Как сделать блокировку строки для использования таблицы в качестве счетчиков? - PullRequest
0 голосов
/ 28 февраля 2020

У меня есть API, который выполняет транзакции с использованием разных провайдеров, и для каждого провайдера я обрабатываю внутренний счетчик в таблице. Проблема возникает потому, что мы получим больше трафика c и у нас будет транзакция с повторной нумерацией.

Запрос, который я использую для счетчика, следующий:

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, например или другим способом, оно также допустимо, мы можем изменить реализацию.

1 Ответ

0 голосов
/ 28 февраля 2020

Ниже приведен пример использования транзакции SERIALIZABLE. Блокировка на обновленном ключе приобретается для обновления, и, когда идентификатор еще не существует, блокировка диапазона ключей приобретается для вставки. Последнее ограничит параллелизм для других вставок в пределах диапазона, но, вероятно, не будет проблемой, поскольку транзакция будет зафиксирована немедленно, снимая блокировки.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...