ЗАКАЗАТЬ ПО И С (ROWLOCK, UPDLOCK, READPAST) - PullRequest
4 голосов
/ 17 июня 2011

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

BEGIN TRANSACTION

CREATE TABLE #Temp (ID INT, SOMEFIELD INT)

INSERT INTO #Temp SELECT TOP (@Something) TableA.ID, TableB.SomeField FROM TableA WITH (ROWLOCK, READPAST) INNER JOIN TableB WITH (UPDLOCK, READPAST) WHERE Condition1

INSERT INTO #Temp SELECT TOP (@Something) TableA.ID, TableB.SomeField FROM TableA WITH (ROWLOCK, READPAST) INNER JOIN TableB WITH (UPDLOCK, READPAST) WHERE Condition2

(...)

UPDATE TableB SET SomeField = 1 FROM TableB WITH (ROWLOCK, READPAST) WHERE ID IN (SELECT ID FROM #Temp)

COMMIT TRANSACTION

Я использую ROWLOCK в первой таблице и UPDLOCK во второй, потому что,после этого выбора я собираюсь обновить только TableB, хотя мне нужно убедиться, что эти строки не обновляются в TableA при любом другом параллельном запросе.Все идет хорошо до того момента, когда мне нужно вставить предложение ORDER BY в любой из SELECT выше, чтобы были выбраны только очень специфические идентификаторы (я действительно должен это сделать).Происходит следующее:

1) Без ORDER BY два параллельных выполнения выполняются по желанию, возвращая разные и непересекающиеся результаты;однако они не возвращают результаты, которые я хочу, потому что эти точные результаты выходили за рамки каждого оператора SELECT.

2) При использовании ORDER BY и двух одновременных выполнениях только первое возвращает результаты.Второй ничего не возвращает.

Я помню, как видел в блоге, что для работы таких запросов с WITH (ROWLOCK, READPAST) и ORDER BY необходимо создать индексы для полей, которые используются в порядке,Я попробовал, но получил те же результаты.Как мне обойти эту проблему?

Редактировать: Например, если у меня есть таблица TestTable с полями (TestID INT, Value INT) и значениями "(1,1), (2,2), ... "и выполнить" одновременно "

BEGIN TRANSACTION

SELECT TOP 2 TestID FROM TestTable WITH (UPDLOCK, READPAST)

WAITFOR DELAY '00:00:05'

COMMIT TRANSACTION

, первое выполнение возвращает строки (1,2), а второе возвращает (3,4) в соответствии с ожиданиями.Однако, если я выполню

BEGIN TRANSACTION

SELECT TOP 2 TestID FROM TestTable WITH (UPDLOCK, READPAST) ORDER BY VALUE ASC

WAITFOR DELAY '00:00:05'

COMMIT TRANSACTION

, первый вернет (1, 2), а второй ничего не вернет.Почему это?!

1 Ответ

6 голосов
/ 17 июня 2011

Как и ожидалось

  • SELECT с ORDER BY, без ROWLOCK, без индекса будет иметь блокировку таблицы из-за сканирования / промежуточной сортировки для выработки TOP 2. Итак, 2-йсеанс пропускает всю таблицу из-за READPAST

  • SELECT без ORDER BY просто выбирает любые 2 строки, которые оказываются в порядке вставки (чистое совпадение, не подразумеваемый порядок).Тот факт, что эти 2 строки заблокированы, заставляет 2-й сеанс переходить к следующим неблокированным строкам.

SQL Server пытается сохранить блокировки настолько детализированными, насколько это возможно, но сканирование означает таблицузамок.Теперь, это обычно не имеет значения (это будет общая блокировка чтения), но у вас также есть UPDLOCK, что означает исключительно заблокированную таблицу

Итак, вам нужны оба этих

  • 3 подсказки в запросах SELECT (ROWLOCK, UPDLOCK, READPAST) для управления гранулярностью, изоляцией и параллелизмом.
    Использование только ROWLOCK все равно приведет к сканированию / сортировке исключительной блокировки в каждой строке.
  • индекс для Value INCLUDE TestID для повышения эффективности SELECT.Только индекс, вероятно, исправит параллелизм, но это не будет гарантировано.

В одном из ваших предыдущих вопросов я связал свой ответ (в комментарии) с Гонка в очереди процессов SQL ServerУсловие , где у меня есть все 3 подсказки блокировки

...