Я тестирую процесс, который удаляет много, много записей одновременно. Он не может TRUNCATE TABLE
, потому что там есть записи, которые должны остаться.
Из-за громкости я разбил удаление на цикл, подобный следующему:
-- Do not block if records are locked.
SET LOCK_TIMEOUT 0
-- This process should be chosen as a deadlock victim in the case of a deadlock.
SET DEADLOCK_PRIORITY LOW
SET NOCOUNT ON
DECLARE @Count
SET @Count = 1
WHILE @Count > 0
BEGIN TRY
BEGIN TRANSACTION -- added per comment below
DELETE TOP (1000) FROM MyTable WITH (ROWLOCK, READPAST) WHERE MyField = SomeValue
SET @Count == @@ROWCOUNT
COMMIT
END TRY
BEGIN CATCH
exec sp_lock -- added to display the open locks after the timeout
exec sp_who2 -- shows the active processes
IF @@TRANCOUNT > 0
ROLLBACK
RETURN -- ignoring this error for brevity
END CATCH
MyTable - это кластеризованная таблица. MyField находится в первом столбце кластерного индекса. Это указывает на логическую группировку записей, поэтому MyField = SomeValue
часто выбирает много записей. Мне все равно, в каком порядке они удаляются, если обрабатывается одна группа за раз. В этой таблице нет других индексов.
Я добавил подсказку ROWLOCK
, чтобы попытаться избежать повышения блокировок, которое мы видели в производстве. Я добавил подсказку READPAST
, чтобы избежать удаления записей, заблокированных другими процессами. Этого никогда не должно случиться, но я пытаюсь быть в безопасности.
Проблема: иногда этот цикл достигает времени ожидания блокировки 1222 «Превышен период ожидания запроса блокировки», когда он работает только *. 1015 *
Я уверен, что в этой системе нет других действий, пока я тестирую этот процесс, потому что это мой собственный ящик разработчика, никто не подключен, другие процессы не выполняются, и профилировщик не показывает активности.
Я могу повторно запустить тот же сценарий через секунду, и он начинает с того места, на котором остановился, удачно удаляя записи - до следующего таймаута блокировки.
Я попытался BEGIN TRY
/ BEGIN CATCH
, чтобы проигнорировать ошибку 1222 и повторить попытку удаления, но она сразу же завершается неудачно с той же ошибкой тайм-аута блокировки. Он также снова завершится ошибкой, если я добавлю небольшую задержку перед повторной попыткой.
Я предполагаю, что время ожидания блокировки связано с чем-то вроде разбиения страницы, но я не уверен, почему это будет конфликтовать с текущей итерацией цикла. Предыдущий оператор удаления уже должен был быть выполнен, и я подумал, что это означает, что любые разбиения страницы также были завершены.
Почему цикл DELETE устанавливает тайм-аут блокировки против себя?
Есть ли способ, которым процесс может избежать этого тайм-аута блокировки или обнаружить, что возобновить безопасно?
Это на SQL Server 2005.
- РЕДАКТИРОВАТЬ -
Я добавил событие Lock: Timeout в профилировщик. Время удаления PAGELOCK во время удаления:
Event Class: Lock:Timeout
TextData: 1:15634 (one example of several)
Mode: 7 - IU
Type: 6 - PAGE
DBCC PAGE сообщает, что эти страницы находятся за пределами диапазона основной базы данных (ID 1).
- РЕДАКТИРОВАТЬ 2 -
Я добавил BEGIN TRY
/ BEGIN CATCH
и набрал exec sp_lock
в блоке catch. Вот что я увидел:
spid dbid ObjId IndId Type Resource Mode Status
19 2 1401108082 1 PAG 1:52841 X GRANT (tempdb.dbo.MyTable)
19 2 1401108082 0 TAB IX GRANT (tempdb.dbo.MyTable)
Me 2 1401108082 0 TAB IX GRANT (tempdb.dbo.MyTable)
Me 1 1115151018 0 TAB IS GRANT (master..spt_values) (?)
SPID 19 - МЕНЕДЖЕР ЗАДАЧ SQL Server. Почему один из этих менеджеров задач получает блокировки на MyTable?