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

Chia các hoạt động xóa lớn thành nhiều phần

Tôi thường xuyên thấy mọi người phàn nàn về cách nhật ký giao dịch của họ chiếm dụng đĩa cứng của họ. Nhiều khi hóa ra họ đang thực hiện một thao tác xóa lớn, chẳng hạn như xóa hoặc lưu trữ dữ liệu, trong một giao dịch lớn.

Tôi muốn chạy một số thử nghiệm để cho thấy tác động, trên cả thời lượng và nhật ký giao dịch, của việc thực hiện cùng một thao tác dữ liệu theo từng phần so với một giao dịch đơn lẻ. Tôi đã tạo cơ sở dữ liệu và điền nó bằng một bảng lớn (SalesOrderDetailEnlarged , từ tập lệnh phóng to AdventureWorks này của Jonathan Kehayias (blog | @SQLPoolBoy)). Bảng này có 4,85 triệu hàng và sử dụng không gian dành riêng là 711 MB (478 MB dữ liệu và 233 MB trong chỉ mục).

Sau khi điền bảng, tôi đã sao lưu cơ sở dữ liệu, sao lưu nhật ký và chạy DBCC SHRINKFILE (đừng bắn tôi) để tác động lên tệp nhật ký có thể được thiết lập từ cơ sở (biết rõ rằng các hoạt động này * sẽ * khiến nhật ký giao dịch phát triển).

Tôi đã cố tình sử dụng một đĩa cơ học thay vì SSD. Mặc dù chúng ta có thể bắt đầu thấy xu hướng chuyển sang SSD phổ biến hơn, nhưng nó vẫn chưa xảy ra trên quy mô đủ lớn; trong nhiều trường hợp, vẫn quá đắt đỏ để làm như vậy trong các thiết bị lưu trữ lớn.

Các bài kiểm tra

Vì vậy, tiếp theo, tôi phải xác định những gì tôi muốn kiểm tra để có tác động lớn nhất. Vì tôi đã tham gia vào một cuộc thảo luận với đồng nghiệp ngày hôm qua về việc xóa dữ liệu theo từng phần, nên tôi đã chọn xóa. Và vì chỉ mục được nhóm trên bảng này nằm trên SalesOrderID , Tôi không muốn sử dụng điều đó - điều đó quá dễ dàng (và rất hiếm khi phù hợp với cách xử lý xóa trong cuộc sống thực). Vì vậy, thay vào đó, tôi quyết định sử dụng một loạt ProductID , điều này sẽ đảm bảo rằng tôi sẽ truy cập một số lượng lớn các trang và yêu cầu nhiều lần ghi nhật ký. Tôi đã xác định những sản phẩm nào cần xóa bằng truy vấn sau:

SELECT TOP (3) 
  ProductID, ProductCount = COUNT(*)
FROM dbo.SalesOrderDetailEnlarged
GROUP BY ProductID
ORDER BY ProductCount DESC;

Điều này mang lại kết quả sau:

ProductID  ProductCount
---------  ------------
870	   187520
712	   135280
873	   134160

Điều này sẽ xóa 456.960 hàng (khoảng 10% của bảng), trải rộng trên nhiều đơn đặt hàng. Đây không phải là một sửa đổi thực tế trong bối cảnh này, vì nó sẽ làm xáo trộn tổng số đơn đặt hàng được tính toán trước và bạn thực sự không thể xóa một sản phẩm khỏi đơn đặt hàng đã được giao. Nhưng bằng cách sử dụng cơ sở dữ liệu mà tất cả chúng ta đều biết và yêu thích, nó tương tự như việc xóa một người dùng khỏi một trang diễn đàn và cũng xóa tất cả các tin nhắn của họ - một kịch bản thực tế mà tôi đã từng thấy.

Vì vậy, một thử nghiệm sẽ là thực hiện thao tác xóa một lần sau:

DELETE dbo.SalesOrderDetailEnlarged WHERE ProductID IN (712, 870, 873);

Tôi biết điều này sẽ yêu cầu một quá trình quét lớn và mất một khoản phí lớn trên nhật ký giao dịch. Đó là loại vấn đề. :-)

Trong khi điều đó đang chạy, tôi đã tập hợp một tập lệnh khác sẽ thực hiện xóa này theo các phần:25.000, 50.000, 75.000 và 100.000 hàng cùng một lúc. Mỗi phần sẽ được cam kết trong giao dịch của riêng nó (vì vậy nếu bạn cần dừng tập lệnh, bạn có thể và tất cả các phần trước đó sẽ được cam kết, thay vì phải bắt đầu lại) và tùy thuộc vào mô hình khôi phục, sẽ được tuân theo bằng CHECKPOINT hoặc BACKUP LOG để giảm thiểu tác động liên tục đến nhật ký giao dịch. (Tôi cũng sẽ kiểm tra mà không có các thao tác này.) Nó sẽ trông giống như thế này (Tôi sẽ không bận tâm đến việc xử lý lỗi và các tiện ích khác cho bài kiểm tra này, nhưng bạn không nên ung dung như vậy):

SET NOCOUNT ON;
 
DECLARE @r INT;
 
SET @r = 1;
 
WHILE @r > 0
BEGIN
  BEGIN TRANSACTION;
 
  DELETE TOP (100000) -- this will change
    dbo.SalesOrderDetailEnlarged
    WHERE ProductID IN (712, 870, 873);
 
  SET @r = @@ROWCOUNT;
 
  COMMIT TRANSACTION;
 
  -- CHECKPOINT;    -- if simple
  -- BACKUP LOG ... -- if full
END

Tất nhiên, sau mỗi lần kiểm tra, tôi sẽ khôi phục bản sao lưu ban đầu của cơ sở dữ liệu WITH REPLACE, RECOVERY , đặt mô hình khôi phục cho phù hợp và chạy thử nghiệm tiếp theo.

Kết quả

Kết quả của bài kiểm tra đầu tiên không gây ngạc nhiên cho lắm. Để thực hiện xóa chỉ trong một câu lệnh, mất 42 giây đầy đủ và 43 giây đơn giản. Trong cả hai trường hợp, điều này đã tăng nhật ký lên 579 MB.

Bộ bài kiểm tra tiếp theo có một vài bất ngờ đối với tôi. Một là, trong khi các phương pháp phân khúc này đã làm giảm đáng kể tác động đến tệp nhật ký, chỉ có một số kết hợp gần hết thời gian và không có kết hợp nào thực sự nhanh hơn. Một điều khác là, nói chung, phân khúc trong khôi phục hoàn toàn (không thực hiện sao lưu nhật ký giữa các bước) hoạt động tốt hơn các hoạt động tương đương trong khôi phục đơn giản. Dưới đây là kết quả về thời lượng và tác động của nhật ký:


Thời lượng, tính bằng giây, của các thao tác xóa khác nhau xóa 457K hàng


Kích thước nhật ký, tính bằng MB, sau nhiều thao tác xóa khác nhau sẽ xóa 457K hàng

Một lần nữa, nói chung, trong khi kích thước nhật ký giảm đáng kể, thời lượng được tăng lên. Bạn có thể sử dụng loại thang đo này để xác định xem điều quan trọng hơn là giảm tác động đến dung lượng ổ đĩa hay giảm thiểu lượng thời gian sử dụng. Đối với một lần truy cập nhỏ trong thời lượng (và xét cho cùng, hầu hết các quy trình này đều được chạy ở chế độ nền), bạn có thể tiết kiệm đáng kể (lên đến 94%, trong các thử nghiệm này) trong việc sử dụng không gian nhật ký.

Lưu ý rằng tôi đã không thử bất kỳ thử nghiệm nào trong số này với tính năng nén được bật (có thể là thử nghiệm trong tương lai!) Và tôi để cài đặt tự động đăng nhập nhật ký ở giá trị mặc định khủng khiếp (10%) - một phần do lười biếng và một phần vì nhiều môi trường ngoài đó đã được giữ lại cài đặt khủng khiếp này.

Nhưng nếu tôi có nhiều dữ liệu hơn thì sao?

Tiếp theo, tôi nghĩ tôi nên kiểm tra điều này trên một cơ sở dữ liệu lớn hơn một chút. Vì vậy, tôi đã tạo một cơ sở dữ liệu khác và tạo một bản sao mới, lớn hơn của dbo.SalesOrderDetailEnlarged . Trên thực tế, lớn hơn gần mười lần. Lần này thay vì khóa chính trên SalesOrderID, SalesorderDetailID , Tôi vừa đặt nó thành một chỉ mục nhóm (để cho phép các bản sao) và điền nó theo cách này:

SELECT c.* 
  INTO dbo.SalesOrderDetailReallyReallyEnlarged 
  FROM AdventureWorks2012.Sales.SalesOrderDetailEnlarged AS c
  CROSS JOIN 
  (
    SELECT TOP 10 Number FROM master..spt_values
  ) AS x;
 
CREATE CLUSTERED INDEX so ON dbo.SalesOrderDetailReallyReallyEnlarged
  (SalesOrderID,SalesOrderDetailID);
 
-- I also made this index non-unique:
CREATE NONCLUSTERED INDEX rg ON dbo.SalesOrderDetailReallyReallyEnlarged(rowguid);
 
CREATE NONCLUSTERED INDEX p ON dbo.SalesOrderDetailReallyReallyEnlarged(ProductID);

Do giới hạn về dung lượng ổ đĩa, tôi đã phải di chuyển máy ảo của máy tính xách tay của mình cho bài kiểm tra này (và chọn hộp 40 lõi, với 128 GB RAM, chỉ xảy ra tình trạng gần như không hoạt động :-)), và vẫn nó không phải là một quá trình nhanh chóng bằng bất kỳ phương tiện nào. Dân số của bảng và tạo chỉ mục mất ~ 24 phút.

Bảng có 48,5 triệu hàng và chiếm 7,9 GB trong đĩa (4,9 GB dữ liệu và 2,9 GB trong chỉ mục).

Lần này, truy vấn của tôi để xác định một tập hợp tốt ProductID ứng cử viên giá trị cần xóa:

SELECT TOP (3) 
  ProductID, ProductCount = COUNT(*)
FROM dbo.SalesOrderDetailReallyReallyEnlarged
GROUP BY ProductID
ORDER BY ProductCount DESC;

Mang lại các kết quả sau:

ProductID  ProductCount
---------  ------------
870	   1828320
712	   1318980
873	   1308060

Vì vậy, chúng tôi sẽ xóa 4.455.360 hàng, dưới 10% của bảng. Theo một mô hình tương tự như thử nghiệm ở trên, chúng tôi sẽ xóa tất cả trong một lần chụp, sau đó theo các phần 500.000, 250.000 và 100.000 hàng.

Kết quả:

Thời lượng, tính bằng giây, của các thao tác xóa khác nhau xóa các hàng 4,5MM

Kích thước nhật ký, tính bằng MB, sau nhiều thao tác xóa khác nhau, loại bỏ các hàng 4,5MM

Vì vậy, một lần nữa, chúng tôi thấy kích thước tệp nhật ký giảm đáng kể (hơn 97% trong các trường hợp có kích thước đoạn nhỏ nhất là 100K); tuy nhiên, ở quy mô này, chúng tôi thấy một số trường hợp chúng tôi cũng thực hiện xóa trong thời gian ngắn hơn, ngay cả với tất cả các sự kiện tự động duyệt đã xảy ra. Điều đó nghe có vẻ giống như win-win đối với tôi!

Lần này với nhật ký lớn hơn

Bây giờ, tôi tò mò làm thế nào những lần xóa khác nhau này sẽ so sánh với một tệp nhật ký được định kích thước trước để đáp ứng cho các hoạt động lớn như vậy. Gắn bó với cơ sở dữ liệu lớn hơn của chúng tôi, tôi đã mở rộng trước tệp nhật ký lên 6 GB, sao lưu nó, sau đó chạy lại các bài kiểm tra:

ALTER DATABASE delete_test MODIFY FILE
(NAME=delete_test_log, SIZE=6000MB);

Kết quả, so sánh thời lượng với tệp nhật ký cố định với trường hợp tệp phải tự động duyệt liên tục:


Thời lượng, tính bằng giây, của các thao tác xóa khác nhau xóa các hàng 4,5MM , so sánh kích thước nhật ký cố định và tự động đăng ký

Một lần nữa, chúng ta thấy rằng các phương thức phân đoạn xóa thành các lô và * không * thực hiện sao lưu nhật ký hoặc một điểm kiểm tra sau mỗi bước, đối thủ với thao tác đơn lẻ tương đương về thời lượng. Trên thực tế, hãy thấy rằng hầu hết thực sự hoạt động trong thời gian tổng thể ít hơn, với phần thưởng bổ sung mà các giao dịch khác sẽ có thể thực hiện và ra giữa các bước. Đó là một điều tốt trừ khi bạn muốn thao tác xóa này chặn tất cả các giao dịch không liên quan.

Kết luận

Rõ ràng là không có câu trả lời chính xác duy nhất cho vấn đề này - có rất nhiều biến "nó phụ thuộc" cố hữu. Có thể mất một số thử nghiệm để tìm ra con số kỳ diệu của bạn, vì sẽ có sự cân bằng giữa chi phí cần thiết để sao lưu nhật ký và lượng công việc và thời gian bạn tiết kiệm ở các kích thước phân đoạn khác nhau. Nhưng nếu bạn đang có kế hoạch xóa hoặc lưu trữ một số lượng lớn các hàng, thì có vẻ như bạn sẽ tốt hơn, về tổng thể, thực hiện các thay đổi theo từng phần, thay vì trong một giao dịch lớn - mặc dù con số thời lượng dường như rằng một hoạt động kém hấp dẫn hơn. Đó không phải là tất cả về thời lượng - nếu bạn không có đủ tệp nhật ký được phân bổ trước và không có không gian để chứa một giao dịch lớn như vậy, có lẽ tốt hơn nhiều là giảm thiểu sự phát triển tệp nhật ký với chi phí là thời lượng, trong trường hợp đó, bạn sẽ muốn bỏ qua biểu đồ thời lượng ở trên và chú ý đến biểu đồ kích thước nhật ký.

Nếu bạn có đủ khả năng cung cấp không gian, bạn vẫn có thể muốn hoặc không muốn định cỡ trước nhật ký giao dịch của mình cho phù hợp. Tùy thuộc vào tình huống, đôi khi sử dụng cài đặt tự động duyệt mặc định trong các thử nghiệm của tôi nhanh hơn một chút so với sử dụng tệp nhật ký cố định với nhiều chỗ trống. Thêm vào đó, có thể khó đoán chính xác số tiền bạn sẽ cần để đáp ứng một giao dịch lớn mà bạn chưa chạy. Nếu bạn không thể kiểm tra một kịch bản thực tế, hãy cố gắng hết sức để hình dung tình huống xấu nhất của bạn - sau đó, để an toàn, hãy nhân đôi nó. Kimberly Tripp (blog | @KimberlyLTripp) có một số lời khuyên tuyệt vời trong bài đăng này:8 Bước để lưu lượng Nhật ký giao dịch tốt hơn - cụ thể là trong bối cảnh này, hãy xem điểm # 6. Bất kể bạn quyết định tính toán các yêu cầu về không gian nhật ký của mình như thế nào, nếu cuối cùng bạn vẫn cần không gian, tốt hơn nên sử dụng nó theo cách có kiểm soát tốt trước, hơn là tạm dừng các quy trình kinh doanh của bạn trong khi chờ đợi tự động điền ( đừng bận tâm nhiều!).

Một khía cạnh rất quan trọng khác của điều này mà tôi đã không đo lường rõ ràng là tác động đến đồng thời - một loạt các giao dịch ngắn hơn, về lý thuyết, sẽ ít tác động hơn đến các hoạt động đồng thời. Mặc dù một lần xóa đơn lẻ mất ít thời gian hơn một chút so với các hoạt động theo đợt dài hơn, nhưng nó giữ tất cả các khóa của nó trong toàn bộ thời gian đó, trong khi các hoạt động theo nhóm sẽ cho phép các giao dịch được xếp hàng đợi khác lẻn vào giữa mỗi giao dịch. Trong một bài đăng trong tương lai, tôi sẽ cố gắng xem xét kỹ hơn về tác động này (và tôi cũng có kế hoạch phân tích sâu hơn).


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Đặt chế độ chờ vật lý bảo vệ dữ liệu chủ động trong kiến ​​trúc một nút RAC - Phần 1

  2. Cách tạo bản sao ảnh chụp nhanh

  3. ODBC 4.0

  4. Đơn giản hóa quy trình kiểm tra đơn vị được lưu trữ chính còn gọi là thủ tục tiện ích

  5. Hoàn thiện SQL. Câu chuyện về thành công và thất bại