Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Batas Waktu Kunci SQL Server Melebihi Penghapusan Rekaman dalam Loop

Saya telah menemukan jawabannya:penghapusan berulang saya bertentangan dengan proses pembersihan hantu.

Menggunakan saran Nicholas, saya menambahkan BEGIN TRANSACTION dan COMMIT . Saya membungkus loop penghapusan dalam BEGIN TRY / BEGIN CATCH . Dalam BEGIN CATCH , tepat sebelum ROLLBACK , saya menjalankan sp_lock dan sp_who2 . (Saya menambahkan perubahan kode pada pertanyaan di atas.)

Ketika proses saya diblokir, saya melihat output berikut:

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

Untuk referensi di masa mendatang, ketika SQL Server menghapus catatan, itu menetapkan sedikit pada mereka untuk hanya menandainya sebagai "catatan hantu". Setiap beberapa menit, proses internal yang disebut pembersihan hantu berjalan untuk mendapatkan kembali halaman catatan yang telah dihapus sepenuhnya (yaitu semua catatan adalah catatan hantu).

Proses pembersihan hantu dibahas di ServerFault dalam pertanyaan ini.

Ini dia Paul Penjelasan S. Randal tentang proses pembersihan hantu.

Proses pembersihan hantu dapat dinonaktifkan dengan tanda pelacakan. Tapi saya tidak harus melakukannya dalam kasus ini.

Saya akhirnya menambahkan batas waktu tunggu kunci 100 ms. Ini menyebabkan waktu tunggu penguncian sesekali dalam proses pembersihan catatan hantu, tetapi itu dapat diterima. Saya juga menambahkan loop kami yang mencoba kembali mengunci batas waktu hingga 5 kali. Dengan dua perubahan ini, proses saya sekarang biasanya selesai. Sekarang hanya mendapat batas waktu jika ada proses yang sangat panjang yang mendorong banyak data di sekitar yang memperoleh kunci tabel atau halaman pada data yang perlu dibersihkan oleh proses saya.

EDIT 20-07-2016

Kode akhir terlihat seperti ini:

-- 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. Cara membuat koneksi ODBC baru ke satu SQL Server jarak jauh

  2. Apa manfaat memiliki bidang varbinary dalam tabel 1-1 yang terpisah?

  3. SQL Server 2005 Menggunakan CHARINDEX() Untuk membagi string

  4. SQL Server 2008 IDENTITY_INSERT AKTIF kesalahan pada Sisipkan

  5. Cara membuat Batasan Kunci Asing dengan ON DELETE CASCADE di SQL Server - Tutorial SQL Server / TSQL Bagian 80