SqlServer, тупик транзакции, когда таблица фактически заблокирована?

StackOverflow https://stackoverflow.com/questions/772770

  •  12-09-2019
  •  | 
  •  

Вопрос

Этот SQL (вызываемый из C#) иногда приводит к взаимоблокировке.Сервер не испытывает большой нагрузки, поэтому используемый подход заключается в максимально возможной блокировке.

   -- Lock to prevent race-conditions when multiple instances of an application calls this SQL:
        BEGIN TRANSACTION 
-- Check that no one has inserted the rows in T1 before me, and that T2 is in a valid state (Test1 != null)
            IF NOT EXISTS (SELECT TOP 1 1 FROM T1 WITH(HOLDLOCK, TABLOCKX) WHERE FKId IN {0}) AND 
            NOT EXISTS(SELECT TOP 1 1 FROM T2 WITH(HOLDLOCK, TABLOCKX) WHERE DbID IN {0} AND Test1 IS NOT NULL) 
            BEGIN
-- Great! Im the first - go insert the row in T1 and update T2 accordingly. Finally write a log to T3 
               INSERT INTO T1(FKId, Status) 
               SELECT DbId, {1} FROM T2 WHERE DbId IN {0}; 

               UPDATE T2 SET LastChangedBy = {2}, LastChangedAt = GETDATE() WHERE DbId IN {0}; 

               INSERT INTO T3 (F1, FKId, F3) 
               SELECT {2}, DbId, GETDATE() FROM T2 WHERE DbId IN {0} ;
            END; 

            -- Select status on the rows so the program can evaluate what just happened
            SELECT FKId, Status FROM T1 WHERE FkId IN {0}; 

        COMMIT TRANSACTION

Я считаю, что проблема в том, что необходимо заблокировать несколько таблиц.

Я немного не уверен, когда таблицы на самом деле блокируются - когда таблица используется в первый раз - или все таблицы блокируются одновременно в BEGIN TRANS?

Это было полезно?

Решение

Блокировки выполняются, когда вы вызываете lock или выбираете с помощью lock, и освобождаются при фиксации или откате.

Вы можете получить мертвую блокировку, если другая процедура сначала заблокируется в T3, а затем в T1 или T2.Затем две транзакции ждут друг друга, чтобы получить ресурс, одновременно блокируя то, что нужно другой.

Вы также можете избежать блокировки таблицы и использовать сериализуемый уровень изоляции.

Другие советы

Использование табличных блокировок может увеличить вероятность возникновения взаимоблокировок...Не все взаимоблокировки вызваны операциями, нарушающими последовательность...Некоторые из них могут быть вызваны (как вы обнаружили) другими действиями, которые пытаются заблокировать только одну запись в той же таблице, которую вы полностью блокируете, поэтому блокировка всей таблицы увеличивает вероятность возникновения этого конфликта.При использовании сериализуемого уровня изоляции блокировки диапазона размещаются в строках индекса, что может предотвратить вставку/удаление другими операциями SQL таким образом, что может вызвать взаимоблокировку двух одновременных операций из одной и той же процедуры, даже если они закодированы для выполнения своих операций. опа в том же порядке...

В любом случае, чтобы выяснить, что именно вызывает взаимоблокировку, установите флаги трассировки SQL Server 1204 и 1222.Это приведет к записи подробной информации в журналы SQL Server о каждой взаимоблокировке, включая сведения о том, какие операторы были задействованы.

Здесь хорошая статья о том, как это сделать.

(Не забудьте отключить эти флажки, когда закончите...)

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top