Блокировка таблицы SQL - PullRequest
2 голосов
/ 21 января 2011

У меня есть вопрос о блокировке SQL Server, касающийся приложения, которое у нас есть.Приложение принимает данные и сохраняет их в таблице SQL Server.Каждому представлению также присваивается специальный каталожный номер (не связанный с полем идентификации в таблице), который представляет собой последовательный буквенно-цифровой номер.Эти числа взяты из другой таблицы и не генерируются во время выполнения.Таким образом, выполняются следующие шаги:

  1. Вставка данных в таблицу отправки
  2. Получение следующего неназначенного номера каталога из таблицы каталога
  3. Присвоение номера каталога представлению в таблице отправки

Все эти шаги выполняются последовательно в одной и той же хранимой процедуре.

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

Что мы можем сделать, чтобы ограничить чрезмерное присвоение каталожных номеров?

Ответы [ 4 ]

4 голосов
/ 21 января 2011

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

set transaction isolation level REPEATABLE READ
begin transaction
select top 1 @catalog_number = catalog_number
  from catalog_numbers with (updlock,rowlock)
  where assigned = 0
update catalog_numbers set assigned = 1 where catalog_number = :catalog_number
commit transaction
1 голос
/ 21 января 2011

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

insert into Catalog () values ()
set @CatalogNumber = scope_identity()

Функция scope_identity будет возвращать идентификатор последней записи, созданной в том же сеансе, поэтому отдельные сеансы могут создавать записи в одно и то же время и при этом иметь правильный идентификатор.

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

0 голосов
/ 22 января 2011

Вот простое решение. Нет расы. Нет блокировки от ограничительного уровня изоляции транзакции. Однако, вероятно, не будет работать на диалектах SQL, кроме T-SQL.

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

Этот метод должен работать для вас: просто сделайте то же самое «блокированное обновление», которое извлекает значение, что-то вроде:

update top 1 CatalogNumber
set in_use            = 1 ,
    @newCatalogNumber = catalog_number
from CatalogNumber
where in_use = 0

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

drop table dbo.PrimaryKeyGenerator
go
create table dbo.PrimaryKeyGenerator
(
  id            varchar(100) not null ,
  current_value int          not null default(1) ,

  constraint PrimaryKeyGenerator_PK primary key clustered ( id ) ,

)
go
drop procedure dbo.GetNewPrimaryKey
go
create procedure dbo.GetNewPrimaryKey

  @name varchar(100)

as

  set nocount                 on
  set ansi_nulls              on
  set concat_null_yields_null on
  set xact_abort              on

  declare
    @uniqueValue int

  --
  -- put the supplied key in canonical form
  --
  set @name = ltrim(rtrim(lower(@name)))

  --
  -- if the name isn't already defined in the table, define it.
  --
  insert dbo.PrimaryKeyGenerator ( id )
  select id = @name
  where not exists ( select *
                     from dbo.PrimaryKeyGenerator pkg
                     where pkg.id = @name
                   )

  --
  -- now, an interlocked update to get the current value and increment the table
  --
  update PrimaryKeyGenerator
  set @uniqueValue  = current_value ,
      current_value = current_value + 1
  where id = @name

  --
  -- return the new unique value to the caller
  --
  return @uniqueValue
go

Чтобы использовать это:

объявить @pk int exec @pk = dbo.GetNewPrimaryKey 'foobar' выберите @ рк

Тривиально изменить его так, чтобы он возвращал набор результатов или возвращал значение через параметр OUTPUT.

0 голосов
/ 21 января 2011

Мне нравится ответ Аракнида.Вы также можете использовать триггер вставки в таблице отправки для достижения этой цели.Триггер будет находиться в области вставки, и вы эффективно внедрите логику для присвоения catalog_number в триггере.Просто хотел выложить здесь свои варианты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...