Как вы блокируете таблицы в SQL Server 2005, и я должен даже сделать это? - PullRequest
13 голосов
/ 12 сентября 2008

Это займет некоторое объяснение. Я создал отдельную пользовательскую очередь сообщений в SQL Server 2005. У меня есть таблица с сообщениями, которые содержат временные метки как для подтверждения, так и для завершения. Хранимая процедура, которую абоненты выполняют для получения следующего сообщения в своей очереди, также подтверждает сообщение. Все идет нормально. Что ж, если система испытывает огромное количество транзакций (тысяч в минуту), разве не возможно, чтобы сообщение было подтверждено другим выполнением хранимой процедуры, в то время как другое подготовлено к самому себе? Позвольте мне помочь, показывая мой код SQL в хранимой процедуре:

--Grab the next message id
declare @MessageId uniqueidentifier
set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);

--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId

--Select the entire message
...
...

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

Ух ты, хоть что-нибудь из этого имело смысл? Это немного сложно выразить словами ...

Ответы [ 7 ]

7 голосов
/ 12 сентября 2008

как то так

--Grab the next message id
begin tran
declare @MessageId uniqueidentifier
select top 1 @MessageId =   ActionMessageId from UnacknowledgedDemands with(holdlock, updlock);

--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId

-- some error checking
commit tran

--Select the entire message
...
...
2 голосов
/ 19 июля 2011

Это похоже на ситуацию, в которой OUTPUT может быть полезным:

-- Acknowledge and grab the next message
declare @message table (
    -- ...your `ActionMessages` columns here...
)
update ActionMessages
set    AcknowledgedTime = getdate()
output INSERTED.* into @message
where  ActionMessageId in (select top(1) ActionMessageId from UnacknowledgedDemands)
  and  AcknowledgedTime is null

-- Use the data in @message, which will have zero or one rows assuming
-- `ActionMessageId` uniquely identifies a row (strongly implied in your question)
...
...

Там мы обновляем и извлекаем строку в той же операции, которая сообщает оптимизатору запросов точно , что мы делаем, позволяя ему выбрать наиболее детализированную блокировку и поддерживать ее для кратчайшего времени. возможное время (Хотя префикс столбца INSERTED, OUTPUT подобен триггерам, выражается через UPDATE как удаление строки и вставка новой.)

Мне нужна дополнительная информация о ваших таблицах ActionMessages и UnacknowledgedDemands (views / TVFs / что угодно), не говоря уже о более глубоких знаниях автоматической блокировки SQL Server, чтобы сказать, необходимо ли это предложение and AcknowledgedTime is null. Это необходимо для защиты от состояния гонки между дополнительным выбором и обновлением. Я уверен, что в этом не было бы необходимости, если бы мы выбирали из ActionMessages (например, where AcknowledgedTime is null с top на update вместо суб-выбора на UnacknowledgedDemands). Я ожидаю, даже если это не нужно, это безвредно.

Обратите внимание, что OUTPUT в SQL Server 2005 и выше. Это то, что вы сказали, что использовали, но если требуется совместимость с гериатрическими установками SQL Server 2000, вам нужно пойти другим путем.

1 голос
/ 24 сентября 2008

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

declare @MessageId uniqueidentifier
select top 1 @MessageId = ActionMessageId from UnacknowledgedDemands

update ActionMessages
  set AcknowledgedTime = getdate()
  where ActionMessageId = @MessageId and AcknowledgedTime is null

if @@rowcount > 0
  /* acknoweldge succeeded */
else
  /* concurrent query acknowledged message before us,
     go back and try another one */

Чем меньше вы заблокируете - тем выше ваш параллелизм.

1 голос
/ 12 сентября 2008

@ Kilhoffer:

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

Редактировать: Кроме того, SQL не обязательно блокирует всю таблицу - он может просто заблокировать необходимые строки. См. здесь для обзора блокировки на сервере SQL.

0 голосов
/ 12 сентября 2008

Подробнее о SQL Server Выберите Блокировка здесь и здесь . SQL Server имеет возможность вызывать блокировку таблицы для выбора. Во время транзакции с таблицей ничего не случится. После завершения транзакции любые вставки или обновления разрешатся самостоятельно.

0 голосов
/ 12 сентября 2008

Должны ли вы действительно обрабатывать вещи один за другим? Разве вам не нужно, чтобы SQL Server подтверждал все неподтвержденные сообщения с текущей датой и возвращал их? (все также в сделке, конечно)

0 голосов
/ 12 сентября 2008

Если вы хотите заключить код в транзакцию, SQL-сервер будет обрабатывать блокировку соответствующих строк или таблиц.

begin transaction

--Grab the next message id
declare @MessageId uniqueidentifier
set @MessageId = (select top(1) ActionMessageId from UnacknowledgedDemands);

--Acknowledge the message
update ActionMessages
set AcknowledgedTime = getdate()
where ActionMessageId = @MessageId

commit transaction

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