Корректный способ генерировать номера заказа в SQL Server

Этот вопрос, конечно, относится к намного более широкому объему, но здесь это.

У меня есть основное приложение электронной коммерции, где пользователи могут, достаточно естественно, разместить заказы. Упомянутые заказы должны иметь уникальное число, которое я пытаюсь генерировать прямо сейчас.

Каждый порядок является Определенным для поставщика. В основном я имею OrderNumberInfo (VendorID, OrderNumber) таблица. Теперь каждый раз, когда клиент размещает заказ, я должен увеличить OrderNumber для particuar Поставщика и возвращают то значение. Естественно, я не хочу, чтобы другие процессы вмешались в меня, таким образом, я должен исключительно заблокировать эту строку так или иначе:

begin tranaction

    declare @n int
    select @n = OrderNumber 
      from OrderNumberInfo 
      where VendorID = @vendorID

    update OrderNumberInfo 
      set OrderNumber = @n + 1 
      where OrderNumber = @n and VendorID = @vendorID

commit transaction

Теперь, я читал о select ... with (updlock rowlock), пессимистическая блокировка, и т.д., но просто не может приспособить все это в четкой картине:

  • Как эти подсказки играют с SQL Server 2008s' изоляция снимка?
  • Они выполняют уровень строки, уровень страницы или даже блокировки уровня таблицы?
  • Как это терпит многочисленных пользователей, пытающихся генерировать числа для единственного Поставщика?
  • Какие уровни изоляции являются соответствующими здесь?
  • И обычно - что путь состоит в том, чтобы сделать такие вещи?

Править

Только сделать немного вещей более ясными:

  • Производительность в этом конкретном углу приложения является абсолютно не проблемой: заказы будут размещаться относительно нечасто и включат дорогой вызов к веб-сервису поставщиков, таким образом, 1 вторая задержка будет довольно терпима
  • У нас действительно должен быть каждый номера заказа поставщиков, чтобы быть независимыми и последовательными
7
задан Anton Gogolev 8 June 2010 в 08:21
поделиться

3 ответа

Обычно я использую что-то вроде этого:

update OrderNumberInfo with (rowlock)
set @OrderNumber = OrderNumber, OrderNumber = OrderNumber + 1
where VendorID = @VendorID

Это не нужно заключать в транзакцию. Фактически, если вы все-таки заключите его в транзакцию, SQL Server начнет удерживать блокировки на таблице и все замедлится. Когда мне нужно сделать что-то подобное в веб-сервисе, я всегда выполняю это в отдельном подключении к базе данных вне любой транзакции, которая может быть открыта в то время, просто для уверенности.

Я считаю (но не доказал), что SQL Server использует защелку, а не транзакцию, чтобы сделать ее атомарной, что должно быть более эффективным.

Если структура вашей таблицы такова, что строка поставщика должна создаваться по запросу, если она не существует, используйте вместо этого следующую логику:

declare @error int, @rowcount int

-- Attempt to read and update the number.
update OrderNumberInfo with (rowlock)
set @OrderNumber = OrderNumber, OrderNumber = OrderNumber + 1
where VendorID = @VendorID

select @error = @@error, @rowcount = @@rowcount
if @error <> 0 begin
    return @error
end

-- If the update succeeded then exit now.
if @rowcount > 0 begin
    return 0
end

-- Insert the row if it doesn't exist yet.
insert into OrderNumberInfo (VendorID, OrderNumber)
select VendorID, 1
where not exists (select null from OrderNumberInfo where VendorID = @VendorID)

select @error = @@error
if @error <> 0 begin
    return @error
end

-- Attempt to read and update the number.
update OrderNumberInfo with (rowlock)
set @OrderNumber = OrderNumber, OrderNumber = OrderNumber + 1
where VendorID = @VendorID

select @error = @@error
if @error <> 0 begin
    return @error
end

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

Заявление об ограничении ответственности: Я использовал это без проблем на SQL Server 7-2005. Я пока не могу комментировать его поведение в 2008 году.

1
ответ дан 7 December 2019 в 07:40
поделиться

Ваше решение создаст потенциальное узкое место для производительности в таблице OrderNumberInfo .

Есть ли какая-то конкретная причина, по которой заказы не могут быть просто столбцом идентификации, возможно, с префиксом идентификатора поставщика на стороне приложения (например, MSFT-232323)?

Единственный недостаток этого подхода заключается в том, что для каждого поставщика Заказы не будут шаблоном «Добавить-1-для-получения-следующего-заказа- #», но мне не известно о каких-либо технических или деловых соображениях, объясняющих, почему это может вызвать проблему, хотя это может привести к порядку в последовательности обработка немного сложнее.

Они все равно будут увеличиваться и быть уникальными для каждого поставщика, что является единственным реальным требованием для идентификатора заказа.

Это, конечно, будет иметь дополнительное побочное преимущество в виде очень простой логики, не зависящей от поставщика (если она у вас когда-либо будет) - например, контроль качества / отчетность в масштабе всего приложения.

3
ответ дан 7 December 2019 в 07:40
поделиться

Вы можете использовать предложение OUTPUT . Это должно делать все атомарно, не требуя транзакции.

-- either return the order number directly as a single column resultset
UPDATE OrderNumberInfo 
SET OrderNumber = OrderNumber + 1
    OUTPUT DELETED.OrderNumber
WHERE VendorID = @vendorID


-- or use an intermediate table variable to get the order number into @n
DECLARE @n INT
DECLARE @temp TABLE ( OrderNumber INT )

UPDATE OrderNumberInfo 
SET OrderNumber = OrderNumber + 1
    OUTPUT DELETED.OrderNumber
    INTO @temp ( OrderNumber )
WHERE VendorID = @vendorID

SET @n = (SELECT TOP 1 OrderNumber FROM @temp)

В приведенных выше примерах предполагается, что столбец VendorID имеет уникальное ограничение или, по крайней мере, будет только одна строка для каждого идентификатора поставщика. Если это не так, вы потенциально будете обновлять и / или возвращать несколько строк, что не кажется хорошей идеей!

3
ответ дан 7 December 2019 в 07:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: