Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Времето за изчакване на заключването на SQL Server е превишено Изтриване на записи в цикъл

Намерих отговора:моето зациклено изтриване е в конфликт с процедурата за почистване на призрак.

Използвайки предложението на Никълъс, добавих BEGIN TRANSACTION и COMMIT . Обвих цикъла за изтриване в BEGIN TRY / BEGIN CATCH . В BEGIN CATCH , точно преди ROLLBACK , изпълних sp_lock и sp_who2 . (Добавих промените в кода във въпроса по-горе.)

Когато процесът ми блокира, видях следния резултат:

spid   dbid   ObjId       IndId  Type Resource                         Mode     Status
------ ------ ----------- ------ ---- -------------------------------- -------- ------
20     2      1401108082  0      TAB                                   IX       GRANT
20     2      1401108082  1      PAG  1:102368                         X        GRANT

SPID  Status     Login HostName BlkBy DBName Command       CPUTime DiskIO
----  ---------- ----- -------- ----- ------ ------------- ------- ------
20    BACKGROUND sa    .        .     tempdb GHOST CLEANUP 31      0

За бъдеща справка, когато SQL Server изтрие записи, той задава малко върху тях, за да ги маркира просто като „призрачни записи“. На всеки няколко минути се изпълнява вътрешен процес, наречен призрачно почистване, за да възстанови страници със записи, които са били напълно изтрити (т.е. всички записи са призрачни записи).

Процесът на почистване на призраци беше обсъден в ServerFault в този въпрос.

Ето го Пол Обяснението на С. Рандал за процеса на почистване на призраци.

Възможно е да деактивирате процеса на почистване на призраци с флаг за проследяване. Но в този случай не трябваше да го правя.

В крайна сметка добавих време за изчакване на заключване от 100 ms. Това причинява случайни изчаквания за заключване в процеса на почистване на призрачен запис, но това е приемливо. Добавих също така нашия цикъл, който прави повторен опит за изчакване на заключването до 5 пъти. С тези две промени моят процес сега обикновено завършва. Сега той получава изчакване само ако има много дълъг процес, който избутва много данни, който придобива блокировки на таблица или страница на данните, които моят процес трябва да изчисти.

РЕДАКТИРАНЕ 20.07.2016

Крайният код изглежда така:

-- Do not block long if records are locked.
SET LOCK_TIMEOUT 100

-- This process volunteers to be a deadlock victim in the case of a deadlock.
SET DEADLOCK_PRIORITY LOW

DECLARE @Error BIT
SET @Error = 0

DECLARE @ErrMsg VARCHAR(1000)
DECLARE @DeletedCount INT
SELECT @DeletedCount = 0

DECLARE @LockTimeoutCount INT
SET @LockTimeoutCount = 0

DECLARE @ContinueDeleting BIT,
    @LastDeleteSuccessful BIT

SET @ContinueDeleting = 1
SET @LastDeleteSuccessful = 1

WHILE @ContinueDeleting = 1
BEGIN
    DECLARE @RowCount INT
    SET @RowCount = 0

    BEGIN TRY

        BEGIN TRANSACTION

        -- The READPAST below attempts to skip over locked records.
        -- However, it might still cause a lock wait error (1222) if a page or index is locked, because the delete has to modify indexes.
        -- The threshold for row lock escalation to table locks is around 5,000 records,
        -- so keep the deleted number smaller than this limit in case we are deleting a large chunk of data.
        -- Table name, field, and value are all set dynamically in the actual script.
        SET @SQL = N'DELETE TOP (1000) MyTable WITH(ROWLOCK, READPAST) WHERE MyField = SomeValue' 
        EXEC sp_executesql @SQL, N'@ProcGuid uniqueidentifier', @ProcGUID

        SET @RowCount = @@ROWCOUNT

        COMMIT

        SET @LastDeleteSuccessful = 1

        SET @DeletedCount = @DeletedCount + @RowCount
        IF @RowCount = 0
        BEGIN
            SET @ContinueDeleting = 0
        END

    END TRY
    BEGIN CATCH

        IF @@TRANCOUNT > 0
            ROLLBACK

        IF Error_Number() = 1222 -- Lock timeout
        BEGIN

            IF @LastDeleteSuccessful = 1
            BEGIN
                -- If we hit a lock timeout, and we had already deleted something successfully, try again.
                SET @LastDeleteSuccessful = 0
            END
            ELSE
            BEGIN
                -- The last delete failed, too.  Give up for now.  The job will run again shortly.
                SET @ContinueDeleting = 0
            END
        END
        ELSE -- On anything other than a lock timeout, report an error.
        BEGIN       
            SET @ErrMsg = 'An error occurred cleaning up data.  Table: MyTable Column: MyColumn Value: SomeValue.  Message: ' + ERROR_MESSAGE() + ' Error Number: ' + CONVERT(VARCHAR(20), ERROR_NUMBER()) + ' Line: ' + CONVERT(VARCHAR(20), ERROR_LINE())
            PRINT @ErrMsg -- this error message will be included in the SQL Server job history
            SET @Error = 1
            SET @ContinueDeleting = 0
        END

    END CATCH

END

IF @Error <> 0
    RAISERROR('Not all data could be cleaned up.  See previous messages.', 16, 1)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Завъртане на таблица заедно със сумата на стойността на колона, когато типът на колоната е nvarchar

  2. Как да създадете дата в SQL Server, дадени ден, месец и година като цели числа

  3. Не може да се свърже от Classic ASP към SQL Server 2008 R2 с помощта на SQL Native Client (Windows 7 - IIS7)

  4. Удостоверяване на SQL Server срещу удостоверяване на Windows:Кое да се използва и кога

  5. Изчисляване на геометрично свързани връщания в SQL SERVER 2008