sql >> Databáze >  >> RDS >> Sqlserver

Překročen časový limit zámku SQL Server Odstraňování záznamů ve smyčce

Našel jsem odpověď:moje mazání ve smyčce je v konfliktu s ghost cleanup proc.

Pomocí Nicholasova návrhu jsem přidal BEGIN TRANSACTION a COMMIT . Smyčku odstranění jsem zabalil do BEGIN TRY / BEGIN CATCH . V BEGIN CATCH , těsně před ROLLBACK , spustil jsem sp_lock a sp_who2 . (Přidal jsem změny kódu v otázce výše.)

Když se můj proces zablokoval, viděl jsem následující výstup:

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

Pro budoucí použití, když SQL Server odstraní záznamy, nastaví na nich bit, aby je označil pouze jako "záznamy duchů". Každých pár minut se spustí interní proces nazvaný ghost cleanup, aby se znovu získaly stránky záznamů, které byly zcela odstraněny (tj. všechny záznamy jsou ghost záznamy).

Proces čištění duchů byl diskutován na ServerFault v této otázce.

Zde je Paul S. Randalovo vysvětlení procesu čištění duchů.

Proces čištění duchů je možné zakázat pomocí příznaku trasování. Ale v tomto případě jsem to dělat nemusel.

Nakonec jsem přidal čekací dobu zámku 100 ms. To způsobuje občasné prodlevy čekání na uzamčení v procesu čištění záznamů duchů, ale to je přijatelné. Také jsem přidal naši smyčku, která se až 5krát pokouší o vypršení časového limitu zámku. S těmito dvěma změnami je můj proces nyní obvykle dokončen. Nyní dojde k vypršení časového limitu pouze v případě, že existuje velmi dlouhý proces, který posouvá velké množství dat a získává zámky tabulky nebo stránky na datech, která můj proces potřebuje vyčistit.

UPRAVIT 2016-07-20

Konečný kód vypadá takto:

-- 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. Zobrazení dat v RecyclerView

  2. Odstranit dotaz Chcete-li odstranit řádky v MySQL

  3. JOOQ Oracle Přesnost čísel a java mapování čísel

  4. Row Goals, část 3:Anti Joins