Предполагается, что Sybase ASE ...
Большая проблема, которую вы, вероятно, захотите рассмотреть, заключается в том, хотите ли вы, чтобы один процесс блокировал всю таблицу во время захвата вашей вершины100 строк или если вы хотите, чтобы другие процессы по-прежнему обращались к таблице?
Другой вопрос: хотите ли вы, чтобы несколько процессов одновременно извлекали 100 строк из таблицы, не блокируя друг друга?
Я предполагаю, что вы a) не хотите блокировать всю таблицу и b) вы можете разрешить нескольким процессам одновременно извлекать строки из таблицы.
1 - если возможно, убедитесь, чтотаблица использует блокировку datarows (по умолчанию обычно allpages );это уменьшит степень детализации блокировок до уровня строки (в отличие от уровня страницы для allpages );таблица должна иметь значение datarows , если вы хотите разрешить нескольким процессам одновременно находить / обновлять строки в таблице
2 - убедитесь, что параметр повышения блокировки в таблице достаточно высок, чтобыубедитесь, что обновление 100 строк одного процесса не блокирует таблицу (sp_setpglockpromote
для всех страниц , sp_setrowlockpromote
для datarows );ключевой момент здесь - убедиться, что ваш update
не переходит в блокировку на уровне таблицы!
3 - когда придет время захватить ваш набор из 100 строк, который вы захотите ... внутритранзакция ... update
100 строк со значением status
, которое является уникальным для вашего сеанса, выберите соответствующие id
, а затем снова обновите status
до «В процессе»
Суть операции выглядит следующим образом:
declare @mysession varchar(10)
select @mysession = convert(varchar(10),@@spid) -- replace @@spid with anything that
-- uniquely identifies your session
set rowcount 100 -- limit the update to 100 rows
begin tran get_my_rows
-- start with an update so that get exclusive access to the desired rows;
-- update the first 100 rows you find with your @@spid
update mytable
set status = @mysession -- need to distinguish your locked rows from
-- other processes; if we used 'In Progress'
-- we wouldn't be able to distinguish between
-- rows update earlier in the day or updated
-- by other/concurrent processes
from mytable readpast -- 'readpast' allows your query to skip over
-- locks held by other processes but it only
-- works for datarows tables
where status = 'na'
-- select your reserved id's and send back to the client/calling process
select id
from mytable
where status = @mysession
-- update your rows with a status of 'In Progress'
update mytable
set status = 'In Progress'
where status = @mysession
commit -- close out txn and release our locks
set rowcount 0 -- set back to default of 'unlimited' rows
Потенциальные проблемы:
, если ваша таблица велика и у вас нет индекса на status
тогда выполнение ваших запросов может занять больше времени, чем необходимо;убедившись, что эскалация блокировки достаточно высока, и вы используете блокировку datarows (так что readpast
работает), вы должны увидеть минимальное блокирование других процессов независимо от того, сколько времени потребуется, чтобы найти нужные строки
с индексом в столбце status
, учтите, что все эти update
s будут вызывать много обновлений индекса, что, вероятно, приведет к дорогостоящему отложенные обновления
при использовании datarows и если ваша эскалация блокировки слишком мала, тогда обновление может просмотреть всю таблицу, что приведет к другому (параллельному) процессуreadpast
заблокировать таблицу и не найти строк для обработки
при использовании все страницы вы не сможете использовать readpast
, поэтому параллельные процессы будут блокироватьсяна ваших блокировках (то есть они не смогут читать вокруг вашей блокировки)
, если у вас есть индекс на status
, и несколько параллельных процессов блокируют разные строки встол, там может бытьвероятность возникновения взаимоблокировок (вероятно, в дереве индексов индекса в столбце status
), что, в свою очередь, потребует кодирования вашего клиента / приложения для ожидания и устранения взаимоблокировок
Подумайте:
, если таблица относительно мала, так что сканирование таблицы не требует больших затрат, вы можете отбросить любой индекс в столбце status
, и это должно уменьшитьнакладные расходы производительности отложенные обновления (связанные с обновлением индексов)
, если вы можете работать с определенным для сеанса значением status
(например, 'In Progress - @mysession '), тогда вы можете исключить 2-й оператор update
(может пригодиться, если вы выполняете отложенные обновления для индексированного столбца status
)
если у вас есть другой столбец (столбцы) в таблице, который вы можете использовать для уникальной идентификации строк вашего сеанса (например, last_updated_by_spid
= @@ spid, last_updated_date
= @mydate - где @mydate изначально установите getdate()
), тогда ваш первый update
сможет установить статус = 'В процессе', select
будет использовать @@ spid и @mydate для предложения where
, а второй update
не будет необходимо [ПРИМЕЧАНИЕ: фактически это то же самое, что Гордон пытается решить с помощью своей колонки session
.]
при условии, что вы можете работать с определенным для сеанса значением status
, рассмотрите возможность использования чего-либо, что позволит вам отслеживать и исправлять потерянные строки (например, строка status
остается «In Progress - @mysession», потому что вызывающий процесс прервался и больше не возвращался (переустанавливал) статус
если вы можете передать список id
обратно вызывающей программе в виде единой строки объединенных id
значений, вы можете использовать описанный мною метод в этом ответе , чтобы добавить id
в @variable во время первого обновления, что позволяет вам установить status
= 'В процессе' в первом обновлении, а также позволяет исключить select
и второе update
как бы вы сказали, какие строки были осиротевшими? Вы можете захотеть обновить (маленький) столбец даты и времени с getdate()
, когда вы выдавали update
; затем, если вы, как правило, ожидаете, что status
будет обновлено, скажем, за 5 минут, у вас может быть процесс мониторинга, который ищет потерянные строки, где status
= 'В процессе' и его было больше, скажем, 10 минут с момента последнего update
Если datarows , readpast
, настройки эскалации блокировок и / или потенциал взаимоблокировки слишком велики, и вы можете жить с краткими блокировками на уровне таблицы, вы можете получить процесс, чтобы получить эксклюзивная блокировка на уровне таблицы перед выполнением операторов update
и select
; эксклюзивная блокировка должна быть получена в рамках пользовательской транзакции, чтобы «удерживать» блокировку на время вашей работы; быстрый пример:
begin tran get_my_rows
-- request an exclusive table lock; wait until it's granted
lock table mytable in exclusive mode
update ...
select ...
update ...
commit