С моей точки зрения у вашего сервера серьезная проблема с производительностью. Даже если предположить, что ни одна из записей в запросе
select some_col with (nolock) where id_col between 57000000 and 57001000
был в памяти, это не должно занять 21 секунд, чтобы последовательно прочитать несколько страниц с диска (ваш кластерный индекс на id_col не должен быть фрагментирован, если это авто-идентификатор, и вы не делали что-то глупое, как добавление «desc» к определению индекса).
Но если вы не можете/не исправите это, моим советом будет сделать обновление в небольших пакетах, например 100-1000 записей (в зависимости от того, сколько времени функция поиска потребляет). Одно обновление/транзакция занимает не более 30 секунд.
Вы видите, что каждое обновление сохраняет эксклюзивную блокировку всех записей, которые она изменила, до завершения транзакции. Если вы не используете явную транзакцию, каждый оператор выполняется в одном автоматическом контексте транзакции, поэтому блокировки освобождаются при выполнении инструкции обновления.
Но вы все равно можете столкнуться с тупиками, в зависимости от того, что делают другие процессы. Если они изменяют более чем одну запись за раз, или даже если они собирают и удерживают блокировки чтения на нескольких строках, вы можете получить взаимоблокировки.
Чтобы избежать взаимоблокировок, ваш оператор обновления должен блокировать все записи, которые он будет изменять сразу. Способ сделать это, чтобы поместить один оператор обновления (с только несколько строк, ограниченных id_col) в сериализуемой сделки как
IF @@TRANCOUNT > 0
-- Error: You are in a transaction context already
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
-- Insert Loop here to work "x" through the id range
BEGIN TRANSACTION
UPDATE SOMETABLE
SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
COMMIT
-- Next loop
-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
UPDATE SOMETABLE
SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE [some_col] = 243 AND id_col >= x
COMMIT
Для каждого обновления для этого потребуется обновление/эксклюзивный ключ-диапазона блокировки на (но только они, поскольку вы ограничиваете обновление с помощью кластерного ключа индекса). Он будет ожидать завершения любых других обновлений в тех же самых записях, а затем получить его блокировку (вызывая блокировку для всех других транзакций, но все же только для данных записей), а затем обновлять записи и освобождать блокировку.
Последний дополнительный оператор важен, поскольку он блокирует блокировку ключа до «бесконечности» и, таким образом, предотвращает даже вставки в конце диапазона во время выполнения инструкции обновления.
Какой уровень транзакции вы используете? Есть ли указатель на some_col? Какой индекс указан в ID_COL? – TToni
id_col - это первичное тождество (растущее целое число) ... индексов нет. readcommit transaction level ... –
Я думаю, что реальная проблема заключается в том, что ваша хранимая процедура, как ожидается, займет несколько ** дней ** для выполнения. Вы уверены, что его нельзя оптимизировать? –