SqlServer, тупик транзакции, когда таблица фактически заблокирована? - PullRequest
0 голосов
/ 21 апреля 2009

Этот 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?

Ответы [ 3 ]

3 голосов
/ 21 апреля 2009

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

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

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

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

1 голос
/ 21 апреля 2009

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

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

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

0 голосов
/ 21 апреля 2009

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

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

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

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