Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Thời gian chờ khóa máy chủ SQL đã vượt quá xóa bản ghi trong vòng lặp

Tôi đã tìm thấy câu trả lời:quá trình xóa vòng lặp của tôi xung đột với chương trình dọn dẹp ma.

Sử dụng đề xuất của Nicholas, tôi đã thêm BEGIN TRANSACTIONCOMMIT . Tôi đã gói vòng lặp xóa trong một BEGIN TRY / BEGIN CATCH . Trong BEGIN CATCH , ngay trước ROLLBACK , Tôi đã chạy sp_locksp_who2 . (Tôi đã thêm các thay đổi mã trong câu hỏi ở trên.)

Khi quy trình của tôi bị chặn, tôi thấy kết quả sau:

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

Để tham khảo trong tương lai, khi SQL Server xóa các bản ghi, nó sẽ thiết lập một chút trên chúng để chỉ đánh dấu chúng là "bản ghi ma". Cứ sau vài phút, một quá trình nội bộ được gọi là dọn dẹp ma chạy để lấy lại các trang bản ghi đã bị xóa hoàn toàn (tức là tất cả các bản ghi đều là bản ghi ma).

Quá trình dọn dẹp ma đã được thảo luận trên ServerFault trong câu hỏi này.

Đây là Paul Giải thích của S. Randal về quy trình dọn dẹp ma.

Có thể tắt quá trình dọn dẹp ma bằng cờ theo dõi. Nhưng tôi không cần phải làm như vậy trong trường hợp này.

Tôi đã kết thúc thêm thời gian chờ khóa là 100 mili giây. Điều này gây ra thời gian chờ khóa không thường xuyên trong quá trình dọn dẹp bản ghi ma, nhưng điều đó có thể chấp nhận được. Tôi cũng đã thêm một vòng lặp của chúng tôi để thử lại khóa thời gian chờ tối đa 5 lần. Với hai thay đổi này, quy trình của tôi bây giờ thường hoàn tất. Bây giờ, nó chỉ hết thời gian chờ nếu có một quá trình rất dài đẩy nhiều dữ liệu xung quanh đó có được bảng hoặc khóa trang trên dữ liệu mà quy trình của tôi cần dọn dẹp.

CHỈNH SỬA 2016-07-20

Mã cuối cùng trông giống như sau:

-- 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. Vì không có tham số mảng Sqlserver, cách tốt nhất để tiếp tục là gì?

  2. SQL Server:Mặt tối của NVARCHAR

  3. Chỉ nhận Tháng và Năm từ SQL DATE

  4. 'TẠO CHẾ ĐỘ XEM' phải là câu lệnh đầu tiên trong lô truy vấn

  5. SQL Server - Thứ tự cột có quan trọng không?