sql >> Database teknologi >  >> RDS >> Sqlserver

SQL Server Lock Timeout overskredet Sletning af poster i en sløjfe

Jeg har fundet svaret:min sløjfede sletning er i konflikt med spøgelsesoprydningsproc.

Ved at bruge Nicholas' forslag tilføjede jeg en BEGIN TRANSACTION og en COMMIT . Jeg pakkede sletteløkken i en BEGIN TRY / BEGIN CATCH . I BEGIN CATCH , lige før en ROLLBACK , jeg kørte sp_lock og sp_who2 . (Jeg tilføjede kodeændringerne i spørgsmålet ovenfor.)

Da min proces blev blokeret, så jeg følgende output:

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

Til fremtidig reference, når SQL Server sletter poster, sætter den lidt på dem for bare at markere dem som "spøgelsesposter". Hvert par minutter kører en intern proces kaldet spøgelsesoprydning for at genvinde sider med poster, der er blevet fuldstændigt slettet (dvs. alle poster er spøgelsesposter).

Ghost-oprydningsprocessen blev diskuteret på ServerFault i dette spørgsmål.

Her er Paul S. Randals forklaring af spøgelsesoprydningsprocessen.

Det er muligt at deaktivere spøgelsesoprydningsprocessen med et sporingsflag. Men det behøvede jeg ikke at gøre i dette tilfælde.

Jeg endte med at tilføje en låseventetimeout på 100 ms. Dette forårsager lejlighedsvise låse-ventetimeouts i spøgelsesregistreringsprocessen, men det er acceptabelt. Jeg tilføjede også en vores loop, der forsøger at låse timeouts igen op til 5 gange. Med disse to ændringer er min proces nu som regel afsluttet. Nu får det kun en timeout, hvis der er en meget lang proces, der presser masser af data rundt, og som får tabel- eller sidelåse på de data, som min proces skal rydde op i.

REDIGERING 2016-07-20

Den endelige kode ser således ud:

-- 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. I mysql viser databaserne; kommandoen viser ikke alle mine databaser

  2. MySQL's now() +1 dag

  3. Lås og transaktion i postgres, der skulle blokere en forespørgsel

  4. MySQL:Fejl 1628:Kommentar til tabel 'kunde' er for lang (max =60)