2012-01-25 35 views
11

Tôi đang chạy lệnh sau để xóa hàng trong lô ra khỏi một bảng lớn (150 triệu hàng):Xóa hàng trong bảng nguyên nhân Locks

DECLARE @RowCount int 
WHILE 1=1 
    BEGIN 
     DELETE TOP (10000) t1 
     FROM table t1 
     INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey 
     WHERE t1.YearProcessed <= 2007 

     SET @RowCount = @@ROWCOUNT 

     IF (@RowCount < 10000) BREAK 
    END 

Bảng này là rất cao sử dụng. Tuy nhiên, nó đang xóa hồ sơ, nhưng nó cũng gây ra khóa trên một số hồ sơ, do đó ném lỗi cho người dùng (mà không được chấp nhận trong môi trường chúng tôi đang ở).

Làm cách nào để xóa các bản ghi cũ hơn mà không gây ra khóa? Tôi có nên giảm kích thước của lô từ 10.000 bản ghi xuống 1000? Kích thước nhật ký hiệu ứng này sẽ như thế nào (chúng tôi có rất ít không gian ổ đĩa cứng còn lại để tăng trưởng nhật ký lớn).

Mọi đề xuất?

Trả lời

7

Tôi đã thấy vấn đề lẻ tẻ tương tự trong quá khứ mà ngay cả trong các mẻ nhỏ 0f 5000 hồ sơ, khóa vẫn sẽ xảy ra. Trong trường hợp của chúng tôi, mỗi lần xóa/cập nhật được chứa trong phần bắt đầu của nó Tran ... Vòng lặp Commit.Để khắc phục sự cố, logic của

WAITFOR TRÌ HOÃN '00: 00: 00: 01'

được đặt ở phía trên cùng của mỗi vòng lặp thông qua và sửa chữa các vấn đề.

+0

Đây có phải là điểm bắt đầu/kết thúc của mỗi lô hoặc xóa từng hàng không? – Sean

1

Tôi nghĩ bạn đang đi đúng hướng.

Nhìn vào hai bài báo này, quá:

và:

http://www.dbforums.com/microsoft-sql-server/985516-deleting-without-locking.html

Trước khi bạn chạy xóa, hãy kiểm tra kế hoạch truy vấn ước tính để xem liệu nó đang làm chỉ mục tìm kiếm xóa hoặc vẫn đang thực hiện quét toàn bộ bảng quét/truy cập.

4

Trước hết - có vẻ như DELETE thực hiện Clustered Index Scan của bạn, tôi khuyên bạn nên làm như sau:

create index [IX.IndexName] ON t1(YearProcessed, PrimaryKey) 

Thứ hai - là có bất kỳ nhu cầu tham gia bảng t2?

Và sau đó sử dụng truy vấn sau đây để xóa các hàng, giả định rằng cột PrimaryKey của bạn là loại INT:

declare @ids TABLE(PrimaryKey INT) 
WHILE 1=1 
    BEGIN 
     INSERT @ids 
     SELECT top 10000 DISTINCT t1.PrimaryKey 
     FROM table t1 
     INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey 
     WHERE t1.YearProcessed <= 2007 

     IF @@ROWCOUNT = 0 BREAK 

     DELETE t1 
     WHERE PrimaryKey in (Select PrimaryKey from @ids) 

     delete from @ids 

    END 

Và đừng quên để loại bỏ bảng t2 từ tham gia nếu nó không phải là cần thiết

Nếu nó vẫn gây ra khóa - sau đó giảm số hàng bị xóa trong mỗi vòng

+0

Tôi thực sự bên tham gia từ một bảng cha một danh sách các từ khóa chính để xóa (mà thực sự là một biến bảng). Tôi vừa đăng hai bảng để đơn giản hóa kịch bản. Rất giống với những gì bạn đã làm, nhưng bạn đã thực hiện một cách tiếp cận khác mà tôi sẽ thử. – Sean

0

Ngoài các đề xuất khác (nhằm giảm công việc khi xóa), bạn cũng có thể định cấu hình SQL Server để không chặn người đọc khác trong khi thực hiện xóa vào lúc có thể.

Điều này có thể được thực hiện bằng cách sử dụng "cô lập bản chụp" mà đã được giới thiệu với SQL Server 2005:

http://msdn.microsoft.com/en-us/library/ms345124%28v=sql.90%29.aspx

+1

Bạn nên lưu ý rằng Phân tách ảnh chụp nhanh yêu cầu thay đổi mức cách ly cho một giao dịch. Đọc Cam kết chụp nhanh cam kết sẽ thay đổi mức cách ly mặc định nhưng yêu cầu mất điện rất ngắn. –

0

Nếu bạn có bất kỳ thứ gì bị xóa tầng, hãy đảm bảo chúng được lập chỉ mục.

Đánh dấu truy vấn DELETE và nhấp vào Display estimated execution plan sẽ hiển thị các chỉ mục được đề xuất - trong trường hợp của tôi, bao gồm một số lần xóa tầng.

Thêm chỉ mục cho những người đã thực hiện xóa nhanh hơn rất nhiều - nhưng tôi vẫn không cố xóa tất cả các hàng cùng một lúc.

0

cách tốt nhất mà tôi đã tìm thấy là biểu mẫu asp.net DeleteExpiredSessions. bạn làm một READUNCOMMITTED chọn và đặt các bản ghi trong một bảng tạm thời, hơn xóa bản ghi bằng cách sử dụng một CURSOR.

ALTER PROCEDURE [dbo].[DeleteExpiredSessions] 
    AS 
     SET NOCOUNT ON 
     SET DEADLOCK_PRIORITY LOW 

     DECLARE @now datetime 
     SET @now = GETUTCDATE() 

     CREATE TABLE #tblExpiredSessions 
     ( 
      SessionID nvarchar(88) NOT NULL PRIMARY KEY 
     ) 

     INSERT #tblExpiredSessions (SessionID) 
      SELECT SessionID 
      FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED) 
      WHERE Expires < @now 

     IF @@ROWCOUNT <> 0 
     BEGIN 
      DECLARE ExpiredSessionCursor CURSOR LOCAL FORWARD_ONLY READ_ONLY 
      FOR SELECT SessionID FROM #tblExpiredSessions 

      DECLARE @SessionID nvarchar(88) 

      OPEN ExpiredSessionCursor 

      FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID 

      WHILE @@FETCH_STATUS = 0 
       BEGIN 
        DELETE FROM [ASPState].dbo.ASPStateTempSessions WHERE SessionID = @SessionID AND Expires < @now 
        FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID 
       END 

      CLOSE ExpiredSessionCursor 

      DEALLOCATE ExpiredSessionCursor 

     END 

     DROP TABLE #tblExpiredSessions 

    RETURN 0 
0

Hãy thử điều này,

DECLARE @RowCount int 
WHILE 1=1 
    BEGIN 
     BEGIN TRANSACTION 
     DELETE TOP (10000) t1 
     FROM table t1 
     INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey 
     WHERE t1.YearProcessed <= 2007 
     END TRANSACTION 
     COMMIT TRANSACTION 
     SET @RowCount = @@ROWCOUNT 

     IF (@RowCount < 10000) BREAK 
    END 
Các vấn đề liên quan