Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Przekroczono limit czasu blokady programu SQL Server podczas usuwania rekordów w pętli

Znalazłem odpowiedź:moje zapętlone usuwanie jest sprzeczne z procedurą czyszczenia ducha.

Korzystając z sugestii Mikołaja, dodałem BEGIN TRANSACTION i COMMIT . Zawinąłem pętlę usuwania w BEGIN TRY / BEGIN CATCH . W BEGIN CATCH , tuż przed ROLLBACK , uruchomiłem sp_lock i sp_who2 . (Dodałem zmiany w kodzie w powyższym pytaniu.)

Kiedy mój proces został zablokowany, zobaczyłem następujące wyjście:

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

Na przyszłość, gdy SQL Server usuwa rekordy, ustawia na nich trochę, aby po prostu oznaczyć je jako „rekordy duchów”. Co kilka minut uruchamiany jest wewnętrzny proces zwany czyszczeniem duchów w celu odzyskania stron rekordów, które zostały całkowicie usunięte (tzn. wszystkie rekordy są rekordami duchów).

Proces czyszczenia duchów został omówiony w tym pytaniu w ServerFault.

Oto Paul Wyjaśnienie S. Randala dotyczące procesu czyszczenia duchów.

Możliwe jest wyłączenie procesu czyszczenia ducha za pomocą flagi śledzenia. Ale w tym przypadku nie musiałem tego robić.

Skończyło się na dodaniu limitu czasu oczekiwania na blokadę 100 ms. Powoduje to sporadyczne przekroczenie limitu czasu oczekiwania na blokadę w procesie czyszczenia rekordu ducha, ale jest to akceptowalne. Dodałem również naszą pętlę, która ponawia próby przekroczenia limitu czasu blokady do 5 razy. Dzięki tym dwóm zmianom mój proces zwykle się kończy. Teraz limit czasu zostaje przekroczony tylko wtedy, gdy trwa bardzo długi proces, który przesyła wiele danych, co powoduje blokadę tabeli lub strony na danych, które mój proces musi wyczyścić.

EDYTUJ 20.07.2016

Ostateczny kod wygląda tak:

-- 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. Jak mogę rozwiązać Albo parametr @objname jest niejednoznaczny, albo zgłoszony @objtype (COLUMN) jest błędny.?

  2. Grupa użytkowników Charlotte SQL Server:Napraw wolne zapytania. Szybko.

  3. Jak mogę monitorować wykonane instrukcje sql na serwerze SQL Server 2005?

  4. Jak rozwiązać problem z niemożliwością przełączenia błędu kodowania podczas wstawiania XML do SQL Server

  5. Zwróć numer tygodnia ISO z daty w SQL Server (T-SQL)