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

Lầm tưởng rằng DROP và TRUNCATE TABLE không được ghi nhật ký

Có một huyền thoại dai dẳng trong thế giới SQL Server rằng cả DROP TABLETRUNCATE TABLE các lệnh không được ghi lại.

Họ không. Cả hai đều được ghi nhật ký đầy đủ nhưng được ghi nhật ký hiệu quả.

Bạn có thể dễ dàng chứng minh điều này cho chính mình. Chạy mã sau để thiết lập cơ sở dữ liệu và bảng thử nghiệm, đồng thời hiển thị rằng chúng tôi có 10.000 hàng trong bảng của mình:

CREATE DATABASE [TruncateTest];
GO
 
ALTER DATABASE [TruncateTest] SET RECOVERY SIMPLE;
GO
 
USE [TruncateTest];
GO
 
CREATE TABLE [TestTable] (
    [c1]    INT IDENTITY,
    [c2]    CHAR (8000) DEFAULT 'a');
GO
 
SET NOCOUNT ON;
GO
 
INSERT INTO [TestTable] DEFAULT VALUES;
GO 10000
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Kết quả:

RowCount
———–
10000

Và sau đó là mã sau, cắt ngắn bảng trong một giao dịch và kiểm tra số lượng hàng:

BEGIN TRAN;
GO
TRUNCATE TABLE [TestTable];
GO
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Kết quả:

RowCount
———–
0

Bây giờ bàn trống. Nhưng tôi có thể khôi phục giao dịch và đặt lại tất cả dữ liệu:

ROLLBACK TRAN;
GO
 
SELECT
    COUNT (*) AS N'RowCount'
FROM
    [TestTable];
GO

Kết quả:

RowCount
———–
10000

Rõ ràng là TRUNCATE hoạt động phải được ghi lại nếu không hoạt động quay lại sẽ không hoạt động.

Vậy quan niệm sai lầm bắt nguồn từ đâu?

Nó xuất phát từ hành vi của DROPTRUNCATE các thao tác trên bảng lớn. Chúng sẽ hoàn thành gần như ngay lập tức và nếu bạn xem nhật ký giao dịch bằng fn_dblog ngay sau đó, bạn sẽ chỉ thấy một số lượng nhỏ các bản ghi nhật ký được tạo từ hoạt động. Con số nhỏ đó không tương quan với kích thước của bảng bị cắt bớt hoặc giảm xuống, vì vậy có vẻ như DROPTRUNCATE hoạt động không được ghi lại.

Nhưng chúng được ghi lại đầy đủ, như tôi đã trình bày ở trên. Vậy các bản ghi nhật ký cho các hoạt động ở đâu?

Câu trả lời là các bản ghi nhật ký sẽ được tạo, không phải ngay lập tức, bởi một cơ chế được gọi là 'thả chậm', đã được thêm vào SQL Server 2000 SP3.

Khi một bảng bị xóa hoặc bị cắt bớt, tất cả các trang tệp dữ liệu được phân bổ cho bảng phải được phân bổ. Cơ chế cho điều này trước SQL Server 2000 SP3 như sau:

Đối với mỗi phạm vi được phân bổ cho bảng

Bắt đầu

Nhận khóa phân bổ eXclusive trên phạm vi



khóa trang cho từng trang trong phạm vi (có được khóa ở chế độ eXclusive và ngay lập tức thả nó ra, đảm bảo không ai khác khóa trang)



KHÔNG ĐƯỢC giải phóng khóa phạm vi, đảm bảo rằng không ai khác có thể sử dụng phạm vi đó



Di chuyển đến phạm vi tiếp theo

Kết thúc

Vì tất cả các khóa phạm vi được giữ cho đến khi kết thúc hoạt động và mỗi khóa chiếm một lượng nhỏ bộ nhớ, nên trình quản lý khóa có thể hết bộ nhớ khi DROP hoặc TRUNCATE của một bảng rất lớn xảy ra. Một số khách hàng SQL Server bắt đầu nhận thấy rằng họ gặp phải tình trạng hết bộ nhớ trên SQL Server 2000, vì các bảng phát triển rất lớn và vượt xa tốc độ phát triển của bộ nhớ hệ thống.

Cơ chế trả chậm mô phỏng DROP hoặc TRUNCATE hoạt động hoàn thành ngay lập tức, bằng cách bỏ chọn các phân bổ cho bảng và đưa chúng vào 'hàng đợi thả chậm', để xử lý sau bởi một tác vụ nền. Thao tác tháo và chuyển này chỉ tạo ra một số ít bản ghi nhật ký. Đây là thao tác đang được thực hiện và quay trở lại trong ví dụ mã của tôi ở trên.
'Tác vụ nền thả chậm' quay lên sau vài giây và xử lý tất cả các trang và phạm vi trên hàng đợi thả chậm lô, đảm bảo rằng hoạt động sẽ không hết bộ nhớ. Tất cả các giao dịch phân bổ này đều được ghi nhật ký đầy đủ, nhưng hãy nhớ rằng việc phân bổ một trang chứa đầy dữ liệu hoặc các bản ghi chỉ mục không ghi nhật ký các lần xóa riêng lẻ của các bản ghi đó; thay vào đó, toàn bộ trang chỉ được đánh dấu là đã được phân bổ trong bản đồ byte phân bổ PFS (Page Free Space) có liên quan.

Từ SQL Server 2000 SP3 trở đi, khi bạn thực hiện DROP hoặc TRUNCATE của một bảng, bạn sẽ chỉ thấy một vài bản ghi nhật ký đang được tạo. Nếu bạn đợi một phút hoặc lâu hơn, sau đó xem lại nhật ký giao dịch, bạn sẽ thấy hàng nghìn bản ghi nhật ký đã được tạo bằng thao tác trả chậm, mỗi bản ghi phân bổ một trang hoặc phạm vi. Hoạt động được ghi lại đầy đủ và hiệu quả.

Dưới đây là một ví dụ, sử dụng tình huống chúng tôi đã tạo ở trên:

CHECKPOINT;
GO
TRUNCATE TABLE [TestTable];
GO
SELECT
    COUNT (*) AS N'LogRecCount'
FROM
    fn_dblog (NULL, NULL);
GO

Kết quả:

LogRecCount

———–

25

Như bạn có thể thấy, rõ ràng không có bản ghi nhật ký nào phân bổ 10.000 trang, cộng với 1.250 phạm vi trong bảng TestTable.

Nếu tôi đợi vài giây rồi chạy fn_dblog mã lại, tôi nhận được:

LogRecCount

———–

3811

Bạn có thể thắc mắc tại sao không có ít nhất 10.000 bản ghi nhật ký - một bản ghi cho mỗi trang được phân bổ. Đó là bởi vì các vị trí giao dịch trang thậm chí được ghi lại một cách hiệu quả - với một bản ghi nhật ký phản ánh các thay đổi phân bổ trang PFS cho 8 trang tệp dữ liệu liên tiếp, thay vì một bản ghi nhật ký cho mỗi trang tệp dữ liệu phản ánh trạng thái phân bổ của nó thay đổi trong trang PFS.

SQL Server luôn cố gắng tạo ra ít nhật ký giao dịch nhất có thể, trong khi vẫn tuân thủ các quy tắc về ghi nhật ký đầy đủ hoặc tối thiểu dựa trên mô hình khôi phục hiện tại. Nếu bạn muốn xem các bản ghi nhật ký thực tế được tạo bởi cơ chế bỏ chuyển và trả chậm, chỉ cần thay thế * cho COUNT (*) trong mã fn_dblog ở trên và tìm kiếm một giao dịch có Tên giao dịch được đặt thành DeferredAllocUnitDrop::Process .

Trong các bài đăng trong tương lai, tôi sẽ thảo luận về các yếu tố bên trong làm cơ sở cho những lầm tưởng dai dẳng khác về các khía cạnh hiệu suất của SQL Server Storage Engine.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. NoSQL là gì và nó được sử dụng như thế nào?

  2. SQL Right Join

  3. Tùy chọn điều chỉnh hiệu suất cơ sở dữ liệu Azure SQL

  4. Số lượng hàng đã đọc / Cảnh báo đọc hàng thực tế trong Plan Explorer

  5. Sử dụng cột giả với máy chủ được liên kết