RID тупик на один запрос TSQL - PullRequest
0 голосов
/ 11 мая 2018

Я нахожусь в тупиковой ситуации со старой версией одного из наших программ по одному и тому же хранимой процедуре, которая в основном выполнялась одновременно двумя разными пользователями.

Запрос 1:

UPDATE [UserExport].[Stat] WITH (ROWLOCK)
    SET BOKey = CustomerID + '|' + ISNULL(Period, '') + '|' + ISNULL(FECode, '')
    WHERE CompanyCode = '001' AND BOKey IS NULL AND UserCode = '53'

Запрос 2:

UPDATE [UserExport].[Stat] WITH (ROWLOCK)
    SET BOKey = CustomerID + '|' + ISNULL(Period, '') + '|' + ISNULL(FECode, '')
    WHERE CompanyCode = '001' AND BOKey IS NULL AND UserCode = '87'

Вывод SQL Profiler (только для версии ridlock)

<ridlock fileid="3" pageid="55475" dbid="7" objectname="PRTE.UserExport.Stat" id="lock1b722b980" mode="X" associatedObjectId="72057594355384320">
  <owner-list>
     <owner id="process4a9708" mode="X"/>
  </owner-list>
  <waiter-list>
     <waiter id="process55d4c8" mode="U" requestType="wait"/>
  </waiter-list>
</ridlock>
<ridlock fileid="3" pageid="155604" dbid="7" objectname="PRTE.UserExport.Stat" id="lock1b7999580" mode="X" associatedObjectId="72057594355384320">
  <owner-list>
     <owner id="process55d4c8" mode="X"/>
  </owner-list>
  <waiter-list>
     <waiter id="process4a9708" mode="U" requestType="wait"/>
  </waiter-list>
</ridlock>

Мы выполняем это на SQL Server 2008 R2, схема UserExport используется в качестве зеркала dbo для хранения данных, которые нужно экспортировать, и ни одна из ее таблиц не имеет индексов или триггеров. Я знаю, что дизайн плохой, на самом деле он был переписан в будущих версиях, как и должно быть, однако я все еще не понимаю, как может произойти взаимоблокировка на RID, так как оба запроса не обновляли одну и ту же строку.

Я прочитал здесь , что оператор UPDATE с подсказкой ROWLOCK может заблокировать больше строк, чем он фактически обновляет при работе с таблицей без индекса, поскольку он также блокирует другие строки чтения, но я не могу найти любое дальнейшее объяснение или способ справиться с ним, только обновив запрос.

Означает ли это, что ОБНОВЛЕНИЕ таблицы без индекса всегда так рискованно?

Или, может быть, я что-то неправильно понял, и есть способ гарантировать блокировку только обновленных строк?

1 Ответ

0 голосов
/ 14 мая 2018

Ваше представление о блокировке больше, чем просто затронутая строка, кажется правильным. Другой пост описывает то же самое: https://stackoverflow.com/a/2335599/744133.

В SQL Server подсказки блокировки применяются к проверенным объектам, а не к ним.

Это важное заявление. Имея индекс, вы избегаете сканирования всей таблицы, но в зависимости от того, что покрывает ваш индекс, вы все равно можете блокировать несколько строк. Например, если у вас может быть один и тот же UserCode в разных CompanyCode, вы можете заблокировать их все, если поместите свой индекс только в UserCode.

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

  1. убедитесь, что в вашей таблице есть индексированный PK. В идеале это будет просто уникальный последовательный идентификатор, назначаемый каждой записи.
  2. для вашего обновления, сначала запустите критерий выбора, чтобы найти набор строк, на которые вы хотите повлиять. Это даст вам набор уникальных ключей.
  3. запустить UPDATE WITH (ROWLOCK) с помощью ПК. Проще всего просто написать программный цикл.

У Oracle есть понятие SELECT FOR UPDATE, которое на самом деле похоже на то, что я описал выше для tsql.

Другой подход состоит в том, чтобы не зависеть от блокировок строк. В основном реализуем оптимистичное параллельное обновление.

Эта идея изложена здесь:

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/optimistic-concurrency

Замки - это, как правило, дорогостоящие операции, с которыми трудно справиться Следовательно, они не так масштабируемы в параллельной среде. Оптимистичные параллельные обновления намного легче и масштабируемы, однако для их реализации требуется дополнительное кодирование.

Одним из способов реализации такой модели является введение столбца версий. Вы также обновляете версию, увеличивая ее, но также устанавливая старую версию как часть условия where. Если обновление не удалось, значит, у нас было одновременное обновление. Вы также можете попробовать использовать метку времени или просто проверить старое значение столбца, который вы пытаетесь обновить.

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