Тупик, вызванный повторной вставкой - PullRequest
2 голосов
/ 20 декабря 2011

Следующее sproc пытается вставить строку в таблицу и сгенерировать случайный идентификатор, который используется для PK в соответствующей таблице.Столкновения со случайно сгенерированными идентификаторами обрабатываются в блоке перехвата, где процедура повторяется / вызывается снова.Теперь это занимает много времени и вызывает взаимные блокировки, потому что блокировки хранятся в течение длительного периода времени.Есть ли способ снять взаимоблокировку непосредственно перед повторной попыткой, чтобы было короткое окно, когда другие потоки могут успешно заблокировать индекс PK?

Ответы [ 2 ]

2 голосов
/ 20 декабря 2011

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

Не могли бы вы опубликовать график взаимоблокировки?Это показывает много информации о процессах блокировки.

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

while 1=1
    begin
    EXEC generateRandomPersonId @PersonId=@PersonId OUTPUT
    if not exists (select * from Persons where PersonId = @PersonID)
        break
    end
1 голос
/ 20 декабря 2011

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

Это подход:

  • заменить рекурсию на петлю
  • использование транзакций

CREATE PROCEDURE addPerson
    (
        @FirstName nvarchar(100),
        @LastName nvarchar(100)
    )
AS

BEGIN
  SET NOCOUNT ON;

  DECLARE @doed bit

  set @doed = 0

  DECLARE @PersonId int

  WHILE @doed = 0

  BEGIN
    -- generate random PersonId
    -- this sproc can generate ids that already exist in the table
    EXEC generateRandomPersonId @PersonId=@PersonId OUTPUT

    BEGIN TRANSACTION ExceptionHandling
    BEGIN TRY       

            INSERT INTO [dbo].[Persons] 
            (
                PersonId,FirstName,LastName
            )
            VALUES 
            (
                @PersonId,@FirstName,@LastName
            )
            COMMIT TRANSACTION ExceptionHandling
        BEGIN CATCH

            ROLLBACK TRANSACTION ExceptionHandling
            -- 
            -- HOW TO RELEASE LOCKS HERE that are still held
            -- for the previous INSERT statement?
            --

            DECLARE @ErrorNumber int, @ErrorMessage nvarchar(2048)
            SELECT  @ErrorNumber=ERROR_NUMBER(), 
                    @ErrorMessage=ERROR_MESSAGE()

                        -- if a race condition happened and 
            -- PersonId happened to be picked already, retry all over again
            IF !(@ErrorNumber = 2601 OR @ErrorNumber = 2627 AND CHARINDEX(N'PK_Persons_PersonId', @ErrorMessage) > 0)
                BEGIN
                   --
                   -- RETRYING HERE participates in a high possibility and 
                   -- occurrence of deadlocks
                   set @doed = 0
                END
                ELSE 
                   -- some other error, rethrow it
                   set @doed = 1
                   EXEC rethrowError
                END
        END CATCH
  END  --end while
 END
GO​

отказ от ответственности: не проверено

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