Giới thiệu
Tua lại dành riêng cho các toán tử ở phía bên trong của một vòng lặp lồng nhau tham gia hoặc áp dụng. Ý tưởng là sử dụng lại các kết quả đã tính toán trước đó từ một phần của kế hoạch thực thi ở nơi có thể an toàn.
Ví dụ chính tắc về toán tử kế hoạch có thể tua lại là Table Spool lười . raison d’être của nó là lưu vào bộ nhớ cache các hàng kết quả từ một cây con kế hoạch, sau đó phát lại các hàng đó trong các lần lặp tiếp theo nếu bất kỳ tham số vòng lặp tương quan nào không thay đổi. Việc phát lại các hàng có thể rẻ hơn việc thực hiện lại cây con đã tạo ra chúng. Để biết thêm thông tin cơ bản về các cuộn hiệu suất này xem bài viết trước của tôi.
Tài liệu cho biết chỉ những toán tử sau mới có thể tua lại:
- Bảng đệm
- Ống đếm hàng
- Ống chỉ mục không phân biệt
- Chức năng có giá trị bảng
- Sắp xếp
- Truy vấn Từ xa
- Khẳng định và Bộ lọc toán tử có Biểu thức khởi động
Ba mục đầu tiên thường là những cuốn sách về hiệu suất, mặc dù chúng có thể được giới thiệu vì những lý do khác (khi chúng có thể háo hức cũng như lười biếng).
Các hàm có giá trị trong bảng sử dụng một biến bảng, có thể được sử dụng để lưu vào bộ nhớ cache và phát lại kết quả trong những trường hợp thích hợp. Nếu bạn quan tâm đến việc tua lại hàm có giá trị bằng bảng, vui lòng xem phần Hỏi &Đáp của tôi về Quản trị viên cơ sở dữ liệu Stack Exchange.
Với những điều khó hiểu, bài viết này chỉ nói về Các loại và khi nào họ có thể tua lại.
Sắp xếp Tua lại
Các loại sử dụng bộ nhớ (bộ nhớ và có thể là đĩa nếu chúng bị tràn) để chúng có một cơ sở có khả năng lưu trữ các hàng giữa các lần lặp vòng lặp. Đặc biệt, về nguyên tắc, đầu ra được sắp xếp có thể được phát lại (cuộn lại).
Tuy nhiên, câu trả lời ngắn gọn cho câu hỏi tiêu đề, "Sắp xếp có tua lại không?" là:
Có, nhưng bạn sẽ không thấy nó thường xuyên.
Sắp xếp các loại
Nội bộ có nhiều loại khác nhau, nhưng đối với mục đích hiện tại của chúng tôi, chỉ có hai loại:
- Sắp xếp trong bộ nhớ (
CQScanInMemSortNew
).- Luôn ở trong bộ nhớ; không thể tràn vào đĩa.
- Sử dụng sắp xếp nhanh thư viện tiêu chuẩn.
- Tối đa 500 hàng và hai trang 8KB tổng cộng.
- Tất cả các đầu vào phải là hằng số thời gian chạy. Thông thường, điều này có nghĩa là toàn bộ cây con sắp xếp phải bao gồm chỉ Quét liên tục và / hoặc Tính toán vô hướng toán tử.
- Chỉ có thể phân biệt rõ ràng trong các kế hoạch thực thi khi kế hoạch trình chiếu chi tiết được kích hoạt (cờ theo dõi 8666). Điều này thêm các thuộc tính bổ sung vào Sắp xếp , một trong số đó là “InMemory =[0 | 1]”.
- Tất cả các loại khác.
(Cả hai loại Sắp xếp toán tử bao gồm Top N Sort của họ và Phân loại riêng biệt biến thể).
Tua lại Hành vi
-
Các loại trong bộ nhớ luôn có thể tua lại khi nó an toàn. Nếu không có tham số vòng lặp tương quan hoặc giá trị tham số không thay đổi so với lần lặp ngay trước đó, kiểu sắp xếp này có thể phát lại dữ liệu được lưu trữ của nó thay vì thực thi lại các toán tử bên dưới nó trong kế hoạch thực thi.
-
Sắp xếp không trong bộ nhớ có thể tua lại khi an toàn, nhưng chỉ khi Sắp xếp toán tử chứa nhiều nhất một hàng . Xin lưu ý một Sắp xếp đầu vào có thể cung cấp một hàng trên một số lần lặp, nhưng không cung cấp các hàng khác. Do đó, hành vi thời gian chạy có thể là một hỗn hợp phức tạp của tua lại và tua lại. Nó hoàn toàn phụ thuộc vào số lượng hàng được cung cấp cho Sắp xếp trên mỗi lần lặp trong thời gian chạy. Nhìn chung, bạn không thể dự đoán Sắp xếp là gì sẽ thực hiện trên mỗi lần lặp lại bằng cách kiểm tra kế hoạch thực thi.
Từ “an toàn” trong các mô tả ở trên có nghĩa là:Không xảy ra thay đổi trong tham số hoặc không có toán tử nào bên dưới Sắp xếp có sự phụ thuộc của giá trị đã thay đổi.
Lưu ý quan trọng về kế hoạch thực hiện
Các kế hoạch thực thi không phải lúc nào cũng báo cáo tua lại (và tua lại) một cách chính xác cho Sắp xếp các toán tử. Nhà điều hành sẽ báo cáo tua lại nếu bất kỳ thông số tương quan nào không thay đổi và tua lại nếu không.
Đối với các sắp xếp không trong bộ nhớ (phổ biến nhất và ở xa), một tua lại được báo cáo sẽ chỉ thực sự phát lại kết quả sắp xếp đã lưu nếu có nhiều nhất một hàng trong bộ đệm đầu ra sắp xếp. Nếu không, sắp xếp sẽ báo cáo tua lại, nhưng cây con sẽ vẫn được thực thi lại hoàn toàn (một rebind).
Để kiểm tra xem có bao nhiêu vòng tua được báo cáo là tua lại thực tế, hãy kiểm tra Số lần thực hiện thuộc tính trên các toán tử bên dưới Sắp xếp .
Lịch sử và lời giải thích của tôi
Sắp xếp Hành vi tua lại của người vận hành có vẻ lạ, nhưng nó đã diễn ra theo cách này từ (ít nhất) SQL Server 2000 đến SQL Server 2019 bao gồm (cũng như Cơ sở dữ liệu Azure SQL). Tôi không thể tìm thấy bất kỳ lời giải thích hoặc tài liệu chính thức nào về nó.
Quan điểm cá nhân của tôi là Sắp xếp tua lại khá tốn kém do máy móc phân loại bên dưới, bao gồm các phương tiện chống tràn và việc sử dụng các giao dịch hệ thống trong tempdb .
Trong hầu hết các trường hợp, trình tối ưu hóa sẽ hoạt động tốt hơn khi giới thiệu bộ đệm hiệu suất rõ ràng khi nó phát hiện khả năng trùng lặp các tham số vòng lặp tương quan. Spools là cách ít tốn kém nhất để lưu vào bộ nhớ cache các kết quả từng phần.
Có thể là có thể phát lại một Sắp xếp kết quả sẽ chỉ tiết kiệm chi phí hơn so với Spool khi Sắp xếp chứa nhiều nhất một hàng. Rốt cuộc, việc sắp xếp một hàng (hoặc không có hàng!) Thực tế không liên quan đến bất kỳ việc sắp xếp nào, vì vậy có thể tránh được phần lớn chi phí.
Suy đoán thuần túy, nhưng ai đó nhất định phải hỏi, vì vậy nó có.
Bản trình diễn 1:Tua lại không chính xác
Ví dụ đầu tiên này có hai biến bảng. Đầu tiên chứa ba giá trị được nhân đôi ba lần trong cột c1
. Bảng thứ hai chứa hai hàng cho mỗi trận đấu trên c2 = c1
. Hai hàng phù hợp được phân biệt bằng một giá trị trong cột c3
.
Nhiệm vụ là trả về hàng từ bảng thứ hai có c3
cao nhất giá trị cho mỗi trận đấu trên c1 = c2
. Mã có lẽ rõ ràng hơn lời giải thích của tôi:
DECLARE @T1 table (c1 integer NOT NULL INDEX i); DECLARE @T2 table (c2 integer NOT NULL, c3 integer NOT NULL); INSERT @T1 (c1) VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); INSERT @T2 (c2, c3) VALUES (1, 1), (1, 2), (2, 3), (2, 4), (3, 5), (3, 6); SELECT T1.c1, CA.c2, CA.c3 FROM @T1 AS T1 CROSS APPLY ( SELECT TOP (1) T2.c2, T2.c3 FROM @T2 AS T2 WHERE T2.c2 = T1.c1 ORDER BY T2.c3 DESC ) AS CA ORDER BY T1.c1 ASC OPTION (NO_PERFORMANCE_SPOOL);
NO_PERFORMANCE_SPOOL
gợi ý là có để ngăn trình tối ưu hóa giới thiệu một ống hiệu suất. Điều này có thể xảy ra với các biến bảng khi ví dụ:cờ theo dõi 2453 được bật hoặc biên dịch trì hoãn biến bảng có sẵn, vì vậy trình tối ưu hóa có thể thấy bản chất thực sự của biến bảng (nhưng không phân phối giá trị).
Kết quả truy vấn hiển thị c2
và c3
các giá trị trả về giống nhau cho mỗi c1
riêng biệt giá trị:
Kế hoạch thực thi thực tế cho truy vấn là:
c1
các giá trị, được trình bày theo thứ tự, khớp với lần lặp trước đó 6 lần và thay đổi 3 lần. Sắp xếp báo cáo điều này là 6 tua lại và 3 tua lại.
Nếu điều này là đúng, thì Bảng quét sẽ chỉ thực hiện 3 lần. Sắp xếp sẽ phát lại (tua lại) kết quả của nó trong 6 lần khác.
Như hiện tại, chúng ta có thể thấy Bảng quét được thực thi 9 lần, một lần cho mỗi hàng từ bảng @T1
. Không có tua lại nào xảy ra ở đây .
Demo 2:Sắp xếp Tua lại
Ví dụ trước không cho phép Sắp xếp để tua lại vì (a) nó không phải là Sắp xếp trong bộ nhớ ; và (b) trên mỗi lần lặp lại của vòng lặp, Sắp xếp chứa hai hàng. Plan Explorer hiển thị tổng cộng 18 hàng từ Bảng quét , hai hàng trên mỗi 9 lần lặp.
Bây giờ chúng ta hãy chỉnh sửa ví dụ để chỉ có một hàng trong bảng @T2
cho mỗi hàng phù hợp từ @T1
:
DECLARE @T1 table (c1 integer NOT NULL INDEX i); DECLARE @T2 table (c2 integer NOT NULL, c3 integer NOT NULL); INSERT @T1 (c1) VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); -- Only one matching row per iteration now INSERT @T2 (c2, c3) VALUES --(1, 1), (1, 2), --(2, 3), (2, 4), --(3, 5), (3, 6); SELECT T1.c1, CA.c2, CA.c3 FROM @T1 AS T1 CROSS APPLY ( SELECT TOP (1) T2.c2, T2.c3 FROM @T2 AS T2 WHERE T2.c2 = T1.c1 ORDER BY T2.c3 DESC ) AS CA ORDER BY T1.c1 ASC OPTION (NO_PERFORMANCE_SPOOL);
Kết quả giống như được hiển thị trước đó vì chúng tôi đã giữ hàng phù hợp được sắp xếp cao nhất trên cột c3
. Kế hoạch thực hiện bề ngoài cũng tương tự, nhưng có một điểm khác biệt quan trọng:
Với một hàng trong Sắp xếp bất kỳ lúc nào, nó có thể tua lại khi tham số tương quan c1
không thay đổi. Quét bảng kết quả là chỉ được thực thi 3 lần.
Lưu ý đến Sắp xếp tạo ra nhiều hàng hơn (9) so với nó nhận được (3). Đây là một dấu hiệu tốt cho thấy một Sắp xếp đã quản lý để lưu vào bộ nhớ cache một tập hợp kết quả một hoặc nhiều lần - tua lại thành công.
Demo 3:Tua lại không có gì
Tôi đã đề cập trước đó rằng một Sắp xếp không có trong bộ nhớ có thể tua lại khi nó chứa tối đa một hàng.
Để thấy điều đó đang hoạt động với không hàng , chúng tôi thay đổi thành OUTER APPLY
và không đặt bất kỳ hàng nào trong bảng @T2
. Vì những lý do sẽ sớm trở nên rõ ràng, chúng tôi cũng sẽ ngừng chiếu cột c2
:
DECLARE @T1 table (c1 integer NOT NULL INDEX i); DECLARE @T2 table (c2 integer NOT NULL, c3 integer NOT NULL); INSERT @T1 (c1) VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); -- No rows added to table @T2 -- No longer projecting c2 SELECT T1.c1, --CA.c2, CA.c3 FROM @T1 AS T1 OUTER APPLY ( SELECT TOP (1) --T2.c2, T2.c3 FROM @T2 AS T2 WHERE T2.c2 = T1.c1 ORDER BY T2.c3 DESC ) AS CA ORDER BY T1.c1 ASC OPTION (NO_PERFORMANCE_SPOOL);
Kết quả bây giờ có NULL
trong cột c3
như mong đợi:
Kế hoạch thực hiện là:
Sắp xếp có thể tua lại mà không có hàng nào trong bộ đệm của nó, do đó, Bảng quét chỉ được thực thi 3 lần, mỗi lần cột c1
giá trị đã thay đổi.
Demo 4:Tua lại tối đa!
Giống như các toán tử khác hỗ trợ tua lại, a Sắp xếp sẽ chỉ rebind cây con của nó nếu thông số tương quan đã thay đổi và cây con phụ thuộc vào giá trị đó bằng cách nào đó.
Khôi phục cột c2
chiếu tới bản trình diễn 3 sẽ hiển thị điều này đang hoạt động:
DECLARE @T1 table (c1 integer NOT NULL INDEX i); DECLARE @T2 table (c2 integer NOT NULL, c3 integer NOT NULL); INSERT @T1 (c1) VALUES (1), (1), (1), (2), (2), (2), (3), (3), (3); -- Still no rows in @T2 -- Column c2 is back! SELECT T1.c1, CA.c2, CA.c3 FROM @T1 AS T1 OUTER APPLY ( SELECT TOP (1) T2.c2, T2.c3 FROM @T2 AS T2 WHERE T2.c2 = T1.c1 ORDER BY T2.c3 DESC ) AS CA ORDER BY T1.c1 ASC OPTION (NO_PERFORMANCE_SPOOL);
Kết quả bây giờ hiển thị hai NULL
tất nhiên là cột:
Kế hoạch thực hiện khá khác nhau:
Lần này, Bộ lọc chứa séc T2.c2 = T1.c1
, thực hiện Quét bảng độc lập giá trị hiện tại của tham số tương quan c1
. Sắp xếp có thể tua lại 8 lần một cách an toàn, nghĩa là quá trình quét chỉ được thực hiện một lần .
Demo 5:Sắp xếp trong bộ nhớ
Ví dụ tiếp theo cho thấy một Sắp xếp trong bộ nhớ nhà điều hành:
DECLARE @T table (v integer NOT NULL); INSERT @T (v) VALUES (1), (2), (3), (4), (5), (6); SELECT T.v, OA.i FROM @T AS T OUTER APPLY ( SELECT TOP (1) X.i FROM ( VALUES (REPLICATE('Z', 1390)), ('0'), ('1'), ('2'), ('3'), ('4'), ('5'), ('6'), ('7'), ('8'), ('9') ) AS X (i) ORDER BY NEWID() ) AS OA OPTION (NO_PERFORMANCE_SPOOL);
Kết quả bạn nhận được sẽ khác nhau giữa các lần thực thi, nhưng đây là một ví dụ:
Điều thú vị là các giá trị trong cột i
sẽ luôn giống nhau - mặc dù ORDER BY NEWID()
mệnh đề.
Bạn có thể đã đoán được lý do cho điều này là Sắp xếp kết quả vào bộ nhớ đệm (tua lại). Kế hoạch thực thi hiển thị Quét liên tục thực hiện chỉ một lần, tạo ra tổng cộng 11 hàng:
Sắp xếp này chỉ có Compute Scalar và Quét liên tục toán tử trên đầu vào của nó để nó là một Sắp xếp trong bộ nhớ . Hãy nhớ rằng những thứ này không giới hạn ở nhiều nhất một hàng - chúng có thể chứa 500 hàng và 16KB.
Như đã đề cập trước đó, không thể thấy rõ ràng liệu một Sắp xếp là Trong bộ nhớ hoặc không bằng cách kiểm tra một kế hoạch thực hiện thường xuyên. Chúng tôi cần đầu ra kế hoạch chi tiết , được kích hoạt với cờ theo dõi không có tài liệu 8666. Khi được bật, các thuộc tính toán tử bổ sung sẽ xuất hiện:
Khi việc sử dụng cờ theo dõi không có tài liệu là không thực tế, bạn có thể suy ra rằng một Sắp xếp là “InMemory” theo Phần bộ nhớ đầu vào của nó bằng 0 và Sử dụng bộ nhớ các phần tử không có sẵn trong chương trình hậu thực thi (trên các phiên bản SQL Server hỗ trợ thông tin đó).
Quay lại kế hoạch thực thi:Không có tham số tương quan nên Sắp xếp được tua lại 5 lần miễn phí, nghĩa là Quét liên tục chỉ được thực hiện một lần. Vui lòng thay đổi TOP (1)
lên TOP (3)
hoặc bất cứ điều gì bạn thích. Việc tua lại có nghĩa là kết quả sẽ giống nhau (được lưu vào bộ nhớ đệm / tua lại) cho mỗi hàng đầu vào.
Bạn có thể bị làm phiền bởi ORDER BY NEWID()
mệnh đề không ngăn tua máy. Đây thực sự là một điểm gây tranh cãi, nhưng không chỉ giới hạn ở các loại. Để có cuộc thảo luận đầy đủ hơn (cảnh báo:có thể có lỗ thỏ), vui lòng xem phần Hỏi &Đáp này. Phiên bản ngắn gọn là đây là một quyết định thiết kế sản phẩm có chủ ý, tối ưu hóa hiệu suất, nhưng có những kế hoạch để làm cho hành vi trở nên trực quan hơn theo thời gian.
Demo 6:Không có phân loại trong bộ nhớ
Điều này giống với bản demo 5, ngoại trừ chuỗi được sao chép dài hơn một ký tự:
DECLARE @T table (v integer NOT NULL); INSERT @T (v) VALUES (1), (2), (3), (4), (5), (6); SELECT T.v, OA.i FROM @T AS T OUTER APPLY ( SELECT TOP (1) X.i FROM ( VALUES -- 1391 instead of 1390 (REPLICATE('Z', 1391)), ('0'), ('1'), ('2'), ('3'), ('4'), ('5'), ('6'), ('7'), ('8'), ('9') ) AS X (i) ORDER BY NEWID() ) AS OA OPTION (NO_PERFORMANCE_SPOOL);
Một lần nữa, kết quả sẽ khác nhau mỗi lần thực hiện, nhưng đây là một ví dụ. Lưu ý i
các giá trị bây giờ không giống nhau:
Ký tự phụ chỉ đủ để đẩy kích thước ước tính của dữ liệu được sắp xếp lên trên 16KB. Điều này có nghĩa là Sắp xếp trong bộ nhớ không thể được sử dụng và các vòng tua sẽ biến mất.
Kế hoạch thực hiện là:
Sắp xếp vẫn báo cáo 5 vòng tua lại nhưng Quét liên tục được thực hiện 6 lần, nghĩa là không có tua lại nào thực sự xảy ra. Nó tạo ra tất cả 11 hàng trên mỗi 6 lần thực thi, tạo ra tổng cộng 66 hàng.
Tóm tắt và Kết luận
Bạn sẽ không thấy Sắp xếp nhà điều hành thực sự tua đi tua lại rất thường xuyên, mặc dù bạn sẽ thấy nó nói rằng nó đã làm khá nhiều.
Hãy nhớ rằng một Sắp xếp thông thường chỉ có thể tua lại nếu an toàn và có tối đa một hàng trong loại vào thời điểm đó. “An toàn” có nghĩa là không có thay đổi trong các tham số tương quan của vòng lặp hoặc không có gì bên dưới Sắp xếp bị ảnh hưởng bởi các thay đổi tham số.
An Sắp xếp trong bộ nhớ có thể hoạt động trên tối đa 500 hàng và 16KB dữ liệu có nguồn gốc từ Quét liên tục và Tính toán vô hướng chỉ các toán tử. Nó cũng sẽ chỉ tua lại khi an toàn (bỏ qua lỗi sản phẩm!) Nhưng không giới hạn ở tối đa một hàng.
Đây có vẻ như là những chi tiết bí truyền, và tôi cho rằng đúng như vậy. Vì vậy, nói rằng, họ đã giúp tôi hiểu một kế hoạch thực thi và tìm thấy các cải tiến hiệu suất tốt hơn một lần. Có lẽ một ngày nào đó bạn cũng sẽ thấy thông tin hữu ích.
Chú ý đến Các loại tạo ra nhiều hàng hơn so với đầu vào của họ!
Nếu bạn muốn xem một ví dụ thực tế hơn về Sắp xếp tua lại dựa trên bản demo mà Itzik Ben-Gan cung cấp trong phần một của Trận đấu gần nhất của anh ấy sê-ri, vui lòng xem Đối sánh gần nhất với các phần thưởng sắp xếp.