Giới thiệu
Đạt được ghi nhật ký tối thiểu với INSERT...SELECT
có thể là một công việc kinh doanh phức tạp. Những cân nhắc được liệt kê trong Hướng dẫn hiệu suất tải dữ liệu vẫn khá toàn diện, mặc dù người ta cũng cần đọc SQL Server 2016, Ghi nhật ký tối thiểu và Tác động của Kích thước hàng loạt trong các hoạt động tải hàng loạt của Parikshit Savjani thuộc Nhóm SQL Server Tiger để có được hình ảnh cập nhật cho SQL Server 2016 trở lên, khi tải hàng loạt vào các bảng lưu trữ hàng được nhóm. Điều đó nói rằng, bài viết này hoàn toàn liên quan đến việc cung cấp chi tiết mới về ghi nhật ký tối thiểu khi tải hàng loạt bảng heap truyền thống (không phải “tối ưu hóa bộ nhớ”) bằng cách sử dụng INSERT...SELECT
. Các bảng có chỉ mục nhóm b-cây được đề cập riêng trong phần hai của loạt bài này.
Bảng đống
Khi chèn các hàng bằng INSERT...SELECT
thành một đống không có chỉ mục không phân biệt, tài liệu nói chung rằng các phần chèn như vậy sẽ được ghi lại một cách tối thiểu miễn là TABLOCK
gợi ý là hiện tại. Điều này được phản ánh trong các bảng tóm tắt có trong Hướng dẫn hiệu suất tải dữ liệu và bài Đội Hổ. Các hàng tóm tắt cho bảng heap không có chỉ mục giống nhau trong cả hai tài liệu (không có thay đổi đối với SQL Server 2016):
TABLOCK
rõ ràng gợi ý không phải là cách duy nhất để đáp ứng yêu cầu về khóa cấp bảng . Chúng tôi cũng có thể đặt ‘khóa bảng khi tải hàng loạt’ tùy chọn cho bảng đích sử dụng sp_tableoption
hoặc bằng cách bật cờ theo dõi được lập thành văn bản 715. (Lưu ý:Các tùy chọn này không đủ để kích hoạt ghi nhật ký tối thiểu khi sử dụng INSERT...SELECT
bởi vì INSERT...SELECT
không hỗ trợ khóa cập nhật hàng loạt).
“Có thể đồng thời” trong phần tóm tắt chỉ áp dụng cho các phương pháp tải hàng loạt khác với INSERT...SELECT
. Không thể tải đồng thời bảng heap với INSERT...SELECT
. Như đã lưu ý trong Hướng dẫn hiệu suất tải dữ liệu , tải hàng loạt với INSERT...SELECT
mất một độc quyền X
khóa trên bàn, không phải là cập nhật hàng loạt BU
cần có khóa đối với tải số lượng lớn đồng thời.
Tất cả những điều đó sang một bên - và giả sử không có lý do nào khác để không mong đợi việc ghi nhật ký tối thiểu khi tải hàng loạt một đống không lập chỉ mục với TABLOCK
(hoặc tương đương) - phần chèn vẫn có thể không được ghi nhật ký tối thiểu…
Ngoại lệ đối với quy tắc
Tập lệnh demo sau sẽ được chạy trên phiên bản phát triển trong cơ sở dữ liệu thử nghiệm mới đặt để sử dụng SIMPLE
mô hình phục hồi. Nó tải một số hàng vào một bảng heap bằng cách sử dụng INSERT...SELECT
với TABLOCK
và báo cáo về các bản ghi nhật ký giao dịch được tạo:
CREATE TABLE dbo.TestHeap ( id integer NOT NULL IDENTITY, c1 integer NOT NULL, padding char(45) NOT NULL DEFAULT '' ); GO -- Clear the log CHECKPOINT; GO -- Insert rows INSERT dbo.TestHeap WITH (TABLOCK) (c1) SELECT TOP (897) CHECKSUM(NEWID()) FROM master.dbo.spt_values AS SV; GO -- Show log entries SELECT FD.Operation, FD.Context, FD.[Log Record Length], FD.[Log Reserve], FD.AllocUnitName, FD.[Transaction Name], FD.[Lock Information], FD.[Description] FROM sys.fn_dblog(NULL, NULL) AS FD; GO -- Count the number of fully-logged rows SELECT [Fully Logged Rows] = COUNT_BIG(*) FROM sys.fn_dblog(NULL, NULL) AS FD WHERE FD.Operation = N'LOP_INSERT_ROWS' AND FD.Context = N'LCX_HEAP' AND FD.AllocUnitName = N'dbo.TestHeap';
Kết quả cho thấy tất cả 897 hàng đã được ghi nhật ký đầy đủ mặc dù rõ ràng đáp ứng tất cả các điều kiện để ghi nhật ký tối thiểu (chỉ một mẫu bản ghi nhật ký được hiển thị vì lý do không gian):
Kết quả tương tự cũng được nhìn thấy nếu việc chèn được lặp lại (tức là không quan trọng nếu bảng heap trống hay không). Kết quả này mâu thuẫn với tài liệu.
Ngưỡng ghi nhật ký tối thiểu cho đống
Số lượng hàng cần thêm trong một INSERT...SELECT
tuyên bố để đạt được ghi nhật ký tối thiểu vào một đống không lập chỉ mục có bật khóa bảng phụ thuộc vào phép tính mà Máy chủ SQL thực hiện khi ước tính tổng kích thước của dữ liệu sẽ được chèn. Các đầu vào cho phép tính này là:
- Phiên bản của SQL Server.
- Số hàng ước tính dẫn đến Chèn toán tử.
- Kích thước hàng mục tiêu trong bảng.
Đối với SQL Server 2012 trở về trước , điểm chuyển tiếp cho bảng cụ thể này là 898 hàng . Thay đổi số trong tập lệnh demo TOP
mệnh đề từ 897 đến 898 tạo ra kết quả sau:
Các mục nhập nhật ký giao dịch được tạo liên quan đến việc phân bổ trang và duy trì Bản đồ phân bổ chỉ mục (IAM) và Dung lượng trống của trang (PFS) cấu trúc. Hãy nhớ rằng ghi nhật ký tối thiểu nghĩa là SQL Server không ghi từng việc chèn hàng riêng lẻ. Thay vào đó, chỉ những thay đổi đối với siêu dữ liệu và cấu trúc phân bổ mới được ghi lại. Thay đổi từ 897 thành 898 hàng cho phép ghi nhật ký tối thiểu cho bảng cụ thể này.
Đối với SQL Server 2014 trở lên , điểm chuyển tiếp là 950 hàng cho bảng này. Chạy INSERT...SELECT
với TOP (949)
sẽ sử dụng ghi nhật ký đầy đủ - thay đổi thành TOP (950)
sẽ tạo ra ghi nhật ký tối thiểu .
Các ngưỡng này không phụ thuộc vào Ước tính số lượng con số mô hình đang sử dụng hoặc mức độ tương thích của cơ sở dữ liệu.
Tính toán Kích thước Dữ liệu
SQL Server có quyết định sử dụng tải số lượng lớn tập hợp hàng hay không - và do đó liệu ghi nhật ký tối thiểu có sẵn hay không - phụ thuộc vào kết quả của một loạt các phép tính được thực hiện trong một phương pháp có tên là sqllang!CUpdUtil::FOptimizeInsert
, trả về true để ghi nhật ký tối thiểu, hoặc sai để ghi nhật ký đầy đủ. Một ngăn xếp cuộc gọi mẫu được hiển thị bên dưới:
Bản chất của bài kiểm tra là:
- Chèn phải dành cho hơn 250 hàng .
- Tổng kích thước dữ liệu chèn phải được tính là ít nhất 8 trang .
Việc kiểm tra hơn 250 hàng chỉ phụ thuộc vào số lượng hàng ước tính đến Chèn bảng nhà điều hành. Điều này được hiển thị trong kế hoạch thực hiện là ‘Số lượng hàng ước tính’ . Hãy cẩn thận với điều này. Dễ dàng tạo ra một kế hoạch với số lượng hàng ước tính thấp, ví dụ:bằng cách sử dụng một biến trong TOP
mệnh đề không có OPTION (RECOMPILE)
. Trong trường hợp đó, trình tối ưu hóa đoán ở 100 hàng, sẽ không đạt đến ngưỡng và do đó ngăn tải hàng loạt và ghi nhật ký tối thiểu.
Tính toán tổng kích thước dữ liệu phức tạp hơn và không khớp 'Kích thước hàng ước tính' chảy vào Chèn bảng nhà điều hành. Cách tính toán được thực hiện hơi khác trong SQL Server 2012 trở về trước so với SQL Server 2014 trở lên. Tuy nhiên, cả hai đều tạo ra kết quả về kích thước hàng khác với kết quả được thấy trong kế hoạch thực thi.
Tính toán Kích thước Hàng
Tổng kích thước dữ liệu chèn được tính bằng cách nhân với số hàng ước tính bằng kích thước hàng tối đa dự kiến . Tính toán kích thước hàng là điểm khác nhau giữa các phiên bản SQL Server.
Trong SQL Server 2012 trở về trước, phép tính được thực hiện bởi sqllang!OptimizerUtil::ComputeRowLength
. Đối với bảng đống thử nghiệm (được thiết kế có chủ ý với các cột không rỗng có độ dài cố định đơn giản bằng cách sử dụng FixedVar ban đầu định dạng lưu trữ hàng) phác thảo của phép tính là:
- Khởi tạo một FixedVar trình tạo siêu dữ liệu.
- Nhận thông tin về loại và thuộc tính cho từng cột trong Chèn bảng luồng đầu vào.
- Thêm các cột và thuộc tính đã nhập vào siêu dữ liệu.
- Hoàn thiện trình tạo và yêu cầu kích thước hàng tối đa.
- Thêm chi phí cho bitmap rỗng và số lượng cột.
- Thêm bốn byte cho hàng bit trạng thái và hàng bù trừ với số lượng dữ liệu cột.
Kích thước Hàng Thực tế
Kết quả của phép tính này có thể phù hợp với kích thước hàng vật lý, nhưng không. Ví dụ:khi tắt tính năng lập phiên bản hàng cho cơ sở dữ liệu:
SELECT DDIPS.index_type_desc, DDIPS.alloc_unit_type_desc, DDIPS.page_count, DDIPS.record_count, DDIPS.min_record_size_in_bytes, DDIPS.max_record_size_in_bytes, DDIPS.avg_record_size_in_bytes FROM sys.dm_db_index_physical_stats ( DB_ID(), OBJECT_ID(N'dbo.TestHeap', N'U'), 0, -- heap NULL, -- all partitions 'DETAILED' ) AS DDIPS;
… Cung cấp kích thước bản ghi là 60 byte trong mỗi hàng của bảng kiểm tra:
Điều này được mô tả trong Ước tính kích thước của một đống:
- Tổng kích thước byte của tất cả độ dài cố định cột = 53 byte:
-
id integer NOT NULL
=4 byte -
c1 integer NOT NULL
=4 byte -
padding char(45) NOT NULL
=45 byte.
-
- Bản đồ bit rỗng = 3 byte :
- =2 + int (( Num_Cols + 7) / 8)
- =2 + int ((3 + 7) / 8)
- =3 byte.
- Tiêu đề hàng = 4 byte .
- Tổng số 53 + 3 + 4 = 60 byte .
Nó cũng khớp với kích thước hàng ước tính được hiển thị trong kế hoạch thực thi:
Chi tiết Tính toán Nội bộ
Tính toán nội bộ được sử dụng để xác định xem tải hàng loạt có được sử dụng hay không sẽ đưa ra một kết quả khác, dựa trên luồng chèn sau thông tin cột thu được bằng cách sử dụng trình gỡ lỗi. Số loại được sử dụng khớp với sys.types
:
- Tổng số độ dài cố định kích thước cột = 66 byte :
- Nhập id 173
binary(8)
=8 byte (nội bộ). - Nhập id 56
integer
=4 byte (nội bộ). - Nhập id 104
bit
=1 byte (nội bộ). - Nhập id 56
integer
=4 byte (id
cột). - Nhập id 56
integer
=4 byte (c1
cột). - Nhập id 175
char(45)
=45 byte (padding
cột).
- Nhập id 173
- Bản đồ bit rỗng = 3 byte (như trước đây).
- Tiêu đề hàng overhead = 4 byte (như trước đây).
- Kích thước hàng được tính toán =66 + 3 + 4 = 73 byte .
Sự khác biệt là luồng đầu vào cung cấp Chèn bảng toán tử chứa ba cột bên trong bổ sung . Chúng được loại bỏ khi chương trình được tạo. Các cột bổ sung tạo nên công cụ định vị chèn bảng , bao gồm đánh dấu (RID hoặc bộ định vị hàng) làm thành phần đầu tiên của nó. Đó là siêu dữ liệu cho phần chèn và cuối cùng không được thêm vào bảng.
Các cột bổ sung giải thích sự khác biệt giữa phép tính được thực hiện bởi OptimizerUtil::ComputeRowLength
và kích thước vật lý của các hàng. Đây có thể được xem là một lỗi :SQL Server không được đếm các cột siêu dữ liệu trong luồng chèn theo kích thước vật lý cuối cùng của hàng. Mặt khác, phép tính có thể chỉ là ước tính nỗ lực cao nhất bằng cách sử dụng cập nhật chung nhà điều hành.
Việc tính toán cũng không tính đến các yếu tố khác như tổng chi phí 14 byte của việc lập phiên bản hàng. Điều này có thể được kiểm tra bằng cách chạy lại tập lệnh demo với một trong hai tính năng cách ly ảnh chụp nhanh hoặc đọc cách ly ảnh chụp nhanh đã cam kết đã bật tùy chọn cơ sở dữ liệu. Kích thước vật lý của hàng sẽ tăng 14 byte (từ 60 byte lên 74), nhưng ngưỡng cho ghi nhật ký tối thiểu vẫn không thay đổi ở 898 hàng.
Tính toán ngưỡng
Bây giờ chúng tôi có tất cả các chi tiết chúng tôi cần để xem tại sao ngưỡng là 898 hàng cho bảng này trên SQL Server 2012 trở về trước:
- 898 hàng đáp ứng yêu cầu đầu tiên cho hơn 250 hàng .
- Kích thước hàng được tính =73 byte.
- Số hàng ước tính =897.
- Tổng kích thước dữ liệu =73 byte * 897 hàng =65481 byte.
- Tổng số trang =65481/8192 =7.9932861328125.
- Đây là mức thấp hơn yêu cầu thứ hai đối với> =8 trang.
- Đối với 898 hàng, số trang là 8.002197265625.
- Đây là > =8 trang vì vậy ghi nhật ký tối thiểu được kích hoạt.
Trong SQL Server 2014 trở lên , những thay đổi là:
- Kích thước hàng được tính toán bởi trình tạo siêu dữ liệu.
- Cột số nguyên nội bộ trong công cụ định vị bảng không còn xuất hiện trong luồng chèn. Điều này đại diện cho uniquifier , chỉ áp dụng cho các chỉ mục. Có vẻ như phần mềm này đã bị xóa để sửa lỗi.
- Kích thước hàng dự kiến thay đổi từ 73 thành 69 byte do cột số nguyên bị bỏ qua (4 byte).
- Kích thước vật lý vẫn là 60 byte. Chênh lệch còn lại là 9 byte được tính bằng cột bên trong RID 8 byte và bit 1 byte bổ sung trong luồng chèn.
Để đạt đến ngưỡng 8 trang với 69 byte mỗi hàng:
- 8 trang * 8192 byte mỗi trang =65536 byte.
- 65535 byte / 69 byte mỗi hàng =949.7971014492754 hàng.
- Do đó, chúng tôi dự kiến có tối thiểu 950 hàng để bật tải hàng loạt bộ hàng cho bảng này trên SQL Server 2014 trở đi.
Tóm tắt và Kết luận
Ngược lại với các phương pháp tải hàng loạt hỗ trợ kích thước hàng loạt , như được đề cập trong bài đăng của Parikshit Savjani, INSERT...SELECT
vào một heap không được lập chỉ mục (trống hoặc không) không phải lúc nào cũng dẫn đến việc ghi nhật ký tối thiểu khi khóa bảng được chỉ định.
Để kích hoạt ghi nhật ký tối thiểu với INSERT...SELECT
, SQL Server phải có hơn 250 hàng với tổng kích thước là ít nhất một phạm vi (8 trang).
Khi tính toán tổng kích thước chèn ước tính (để so sánh với ngưỡng 8 trang), SQL Server nhân số hàng ước tính với kích thước hàng tối đa được tính toán. Máy chủ SQL đếm các cột nội bộ hiện trong luồng chèn khi tính toán kích thước hàng. Đối với SQL Server 2012 trở về trước, điều này thêm 13 byte mỗi hàng. Đối với SQL Server 2014 trở lên, nó thêm 9 byte mỗi hàng. Điều này chỉ ảnh hưởng đến việc tính toán; nó không ảnh hưởng đến kích thước vật lý cuối cùng của các hàng.
Khi tải hàng loạt đống được ghi nhật ký tối thiểu đang hoạt động, Máy chủ SQL không chèn từng hàng một. Các nội dung mở rộng được phân bổ trước và các hàng sẽ chèn được thu thập vào các trang hoàn toàn mới bởi sqlmin!RowsetBulk
trước khi được thêm vào cấu trúc hiện có. Một ngăn xếp cuộc gọi mẫu được hiển thị bên dưới:
Các lần đọc logic không được báo cáo cho bảng mục tiêu khi tải hàng loạt đống được ghi nhật ký tối thiểu được sử dụng - Chèn bảng toán tử không cần phải đọc một trang hiện có để xác định vị trí điểm chèn cho mỗi hàng mới.
Các kế hoạch thực thi hiện không hiển thị số lượng hàng hoặc trang đã được chèn bằng cách sử dụng tải hàng loạt bộ hàng và ghi nhật ký tối thiểu . Có lẽ thông tin hữu ích này sẽ được thêm vào sản phẩm trong một bản phát hành trong tương lai.