Cách tối ưu hóa truy vấn T-SQL bằng Kế hoạch thực hiện


15

Tôi có một truy vấn SQL mà tôi đã dành hai ngày qua để cố gắng tối ưu hóa bằng cách sử dụng thử và lỗi và kế hoạch thực hiện, nhưng không có kết quả. Xin vui lòng tha thứ cho tôi vì đã làm điều này nhưng tôi sẽ đăng toàn bộ kế hoạch thực hiện ở đây. Tôi đã nỗ lực để đặt tên bảng và cột trong kế hoạch truy vấn và thực thi chung chung cho ngắn gọn và để bảo vệ IP của công ty tôi. Kế hoạch thực hiện có thể được mở bằng SQL Sentry Plan Explorer .

Tôi đã thực hiện một số lượng T-SQL khá lớn, nhưng sử dụng các kế hoạch thực hiện để tối ưu hóa truy vấn của tôi là một lĩnh vực mới đối với tôi và tôi đã thực sự cố gắng hiểu cách thực hiện. Vì vậy, nếu bất cứ ai có thể giúp tôi với điều này và giải thích cách giải mã kế hoạch thực hiện này để tìm cách trong truy vấn để tối ưu hóa nó, tôi sẽ biết ơn mãi mãi. Tôi có nhiều truy vấn khác để tối ưu hóa - tôi chỉ cần một bàn đạp để giúp tôi với câu hỏi đầu tiên này.

Đây là truy vấn:

DECLARE @Param0 DATETIME     = '2013-07-29';
DECLARE @Param1 INT          = CONVERT(INT, CONVERT(VARCHAR, @Param0, 112))
DECLARE @Param2 VARCHAR(50)  = 'ABC';
DECLARE @Param3 VARCHAR(100) = 'DEF';
DECLARE @Param4 VARCHAR(50)  = 'XYZ';
DECLARE @Param5 VARCHAR(100) = NULL;
DECLARE @Param6 VARCHAR(50)  = 'Text3';

SET NOCOUNT ON

DECLARE @MyTableVar TABLE
(
    B_Var1_PK int,
    Job_Var1 varchar(512),
    Job_Var2 varchar(50)
)

INSERT INTO @MyTableVar (B_Var1_PK, Job_Var1, Job_Var2) 
SELECT B_Var1_PK, Job_Var1, Job_Var2 FROM [fn_GetJobs] (@Param1, @Param2, @Param3, @Param4, @Param6);

CREATE TABLE #TempTable
(
    TTVar1_PK INT PRIMARY KEY,
    TTVar2_LK VARCHAR(100),
    TTVar3_LK VARCHAR(50),
    TTVar4_LK INT,
    TTVar5 VARCHAR(20)
);

INSERT INTO #TempTable
SELECT DISTINCT
    T.T1_PK,
    T.T1_Var1_LK,
    T.T1_Var2_LK,
    MAX(T.T1_Var3_LK),
    T.T1_Var4_LK
FROM
    MyTable1 T
    INNER JOIN feeds.MyTable2 A ON A.T2_Var1 = T.T1_Var4_LK
    INNER JOIN @MyTableVar B ON B.Job_Var2 = A.T2_Var2 AND B.Job_Var1 = A.T2_Var3
GROUP BY T.T1_PK, T.T1_Var1_LK, T.T1_Var2_LK, T.T1_Var4_LK

-- This is the slow statement...
SELECT 
    CASE E.E_Var1_LK
        WHEN 'Text1' THEN T.TTVar2_LK + '_' + F.F_Var1
        WHEN 'Text2' THEN T.TTVar2_LK + '_' + F.F_Var2
        WHEN 'Text3' THEN T.TTVar2_LK
    END,
    T.TTVar4_LK,
    T.TTVar3_LK,
    CASE E.E_Var1_LK
        WHEN 'Text1' THEN F.F_Var1
        WHEN 'Text2' THEN F.F_Var2
        WHEN 'Text3' THEN T.TTVar5
    END,
    A.A_Var3_FK_LK,
    C.C_Var1_PK,
    SUM(CONVERT(DECIMAL(18,4), A.A_Var1) + CONVERT(DECIMAL(18,4), A.A_Var2))
FROM #TempTable T
    INNER JOIN TableA (NOLOCK) A ON A.A_Var4_FK_LK  = T.TTVar1_PK
    INNER JOIN @MyTableVar     B ON B.B_Var1_PK     = A.Job
    INNER JOIN TableC (NOLOCK) C ON C.C_Var2_PK     = A.A_Var5_FK_LK
    INNER JOIN TableD (NOLOCK) D ON D.D_Var1_PK     = A.A_Var6_FK_LK
    INNER JOIN TableE (NOLOCK) E ON E.E_Var1_PK     = A.A_Var7_FK_LK  
    LEFT OUTER JOIN feeds.TableF (NOLOCK) F ON F.F_Var1 = T.TTVar5
WHERE A.A_Var8_FK_LK = @Param1
GROUP BY
    CASE E.E_Var1_LK
        WHEN 'Text1' THEN T.TTVar2_LK + '_' + F.F_Var1
        WHEN 'Text2' THEN T.TTVar2_LK + '_' + F.F_Var2
        WHEN 'Text3' THEN T.TTVar2_LK
    END,
    T.TTVar4_LK,
    T.TTVar3_LK,
    CASE E.E_Var1_LK 
        WHEN 'Text1' THEN F.F_Var1
        WHEN 'Text2' THEN F.F_Var2
        WHEN 'Text3' THEN T.TTVar5
    END,
    A.A_Var3_FK_LK, 
    C.C_Var1_PK


IF OBJECT_ID(N'tempdb..#TempTable') IS NOT NULL
BEGIN
    DROP TABLE #TempTable
END
IF OBJECT_ID(N'tempdb..#TempTable') IS NOT NULL
BEGIN
    DROP TABLE #TempTable
END

Những gì tôi đã tìm thấy là tuyên bố thứ ba (nhận xét là chậm) là phần mất nhiều thời gian nhất. Hai tuyên bố trước khi trở lại gần như ngay lập tức.

Kế hoạch thực hiện có sẵn dưới dạng XML tại liên kết này .

Tốt hơn là nhấp chuột phải và lưu và sau đó mở trong SQL Sentry Plan Explorer hoặc một số phần mềm xem khác thay vì mở trong trình duyệt của bạn.

Nếu bạn cần thêm thông tin từ tôi về các bảng hoặc dữ liệu, xin đừng ngần ngại hỏi.


2
Số liệu thống kê của bạn là cách. Lần cuối bạn hủy chỉ mục hoặc chỉ số cập nhật là khi nào? Ngoài ra, tôi sẽ thử sử dụng bảng tạm thời, thay vì biến bảng, @MyTableVar, vì trình tối ưu hóa thực sự không thể sử dụng số liệu thống kê về các biến của bảng.
Adam Haines

Cảm ơn bạn đã trả lời Adam. Thay đổi @MyTableVar thành bảng tạm thời không có tác dụng gì, nhưng đó chỉ là một số lượng nhỏ hàng (có thể nhìn thấy từ kế hoạch thực hiện). Điều gì trong kế hoạch thực hiện cho thấy rằng số liệu thống kê của tôi đang giảm? Liệu nó chỉ ra những chỉ mục nào sẽ được tổ chức lại hoặc xây dựng lại, và bảng nào sẽ được cập nhật số liệu thống kê?
Neo

3
Phép nối băm đó ở dưới cùng bên phải có khoảng 24.000 hàng trong đầu vào bản dựng nhưng thực tế là 3.285.620 nên có thể bị tràn ra tempdb. tức là các ước tính cho các hàng kết quả từ sự nối giữa TableA@MyTableVarđang tắt. Ngoài ra, số lượng các hàng đi vào các loại lớn hơn nhiều so với ước tính vì vậy chúng cũng có thể bị đổ.
Martin Smith

Câu trả lời:


21

Trước khi đi đến câu trả lời chính, có hai phần mềm bạn cần cập nhật.

Cập nhật phần mềm cần thiết

Đầu tiên là SQL Server. Bạn đang chạy SQL Server 2008 Gói dịch vụ 1 (bản dựng 2531). Bạn phải được vá tối thiểu ít nhất là Gói dịch vụ hiện tại (SQL Server 2008 Gói dịch vụ 3 - bản dựng 5500). Bản dựng gần đây nhất của SQL Server 2008 tại thời điểm viết là Gói dịch vụ 3, Bản cập nhật tích lũy 12 (bản dựng 5844).

Phần thứ hai của phần mềm là SQL Sentry Plan Explorer . Các phiên bản mới nhất có các tính năng và sửa lỗi mới đáng kể, bao gồm khả năng tải trực tiếp gói kế hoạch truy vấn để phân tích chuyên gia (không cần dán XML ở bất cứ đâu!)

Phân tích kế hoạch truy vấn

Ước tính cardinality cho biến bảng là hoàn toàn chính xác, nhờ vào biên dịch lại mức câu lệnh:

ước tính biến bảng

Thật không may, các biến bảng không duy trì số liệu thống kê phân phối, vì vậy tất cả các trình tối ưu hóa đều biết là có sáu hàng; nó không biết gì về các giá trị có thể nằm trong sáu hàng đó. Thông tin này rất quan trọng được đưa ra rằng hoạt động tiếp theo là tham gia vào một bảng khác. Ước tính cardinality từ tham gia đó dựa trên dự đoán hoang dã của trình tối ưu hóa:

ước tính tham gia đầu tiên

Từ thời điểm đó, kế hoạch được chọn bởi trình tối ưu hóa dựa trên thông tin không chính xác, vì vậy không có gì đáng ngạc nhiên khi hiệu suất thực sự quá kém. Cụ thể, bộ nhớ dành riêng cho các loại và bảng băm cho phép nối băm sẽ quá nhỏ. Tại thời điểm thực hiện, các hoạt động sắp xếp và băm tràn sẽ được tràn ra đĩa tempdb vật lý .

SQL Server 2008 không nêu bật điều này trong các kế hoạch thực hiện; bạn có thể theo dõi sự cố tràn bằng Cảnh báo mở rộng hoặc Cảnh báo sắp xếp hồ sơcảnh báo Hash . Bộ nhớ được dành riêng cho các loại và băm dựa trên ước tính cardinality trước khi bắt đầu thực thi và không thể tăng lên trong khi thực hiện bất kể bộ nhớ SQL Server của bạn có bao nhiêu bộ nhớ dự phòng. Do đó, ước tính số lượng hàng chính xác là rất quan trọng đối với bất kỳ kế hoạch thực hiện nào liên quan đến các hoạt động tiêu thụ bộ nhớ không gian làm việc.

Truy vấn của bạn cũng được tham số hóa. Bạn nên xem xét thêm OPTION (RECOMPILE)vào truy vấn nếu các giá trị tham số khác nhau ảnh hưởng đến kế hoạch truy vấn. Có lẽ bạn nên cân nhắc sử dụng nó, vì vậy trình tối ưu hóa có thể thấy giá trị của @Param1thời gian biên dịch. Nếu không có gì khác, điều này có thể giúp trình tối ưu hóa đưa ra ước tính hợp lý hơn cho tìm kiếm chỉ mục được hiển thị ở trên, với điều kiện là bảng rất lớn và được phân vùng. Nó cũng có thể cho phép loại bỏ phân vùng tĩnh.

Hãy thử lại truy vấn với một bảng tạm thời thay vì biến bảng OPTION (RECOMPILE) . Bạn cũng nên thử cụ thể hóa kết quả của lần đầu tiên tham gia vào một bảng tạm thời khác và chạy phần còn lại của truy vấn dựa vào đó. Số lượng hàng không quá lớn (3.285.620), vì vậy điều này sẽ nhanh chóng hợp lý. Trình tối ưu hóa sau đó sẽ có một ước tính chính xác về số liệu thống kê và phân phối chính xác cho kết quả của việc tham gia. Với may mắn, phần còn lại của kế hoạch sẽ rơi vào vị trí độc đáo.

Làm việc từ các thuộc tính được hiển thị trong kế hoạch, truy vấn cụ thể hóa sẽ là:

SELECT
    A.A_Var7_FK_LK,
    A.A_Var4_FK_LK,
    A.A_Var6_FK_LK, 
    A.A_Var5_FK_LK,
    A.A_Var1,
    A.A_Var2,
    A.A_Var3_FK_LK
INTO #AnotherTempTable
FROM @MyTableVar AS B
JOIN TableA AS A
    ON A.Job = B.B_Var1_PK
WHERE
    A_Var8_FK_LK = @Param1;

Bạn cũng có thể INSERTvào một bảng tạm thời được xác định trước (các loại dữ liệu chính xác không được hiển thị trong kế hoạch, vì vậy tôi không thể thực hiện phần đó). Bảng tạm thời mới có thể hoặc không thể hưởng lợi từ các chỉ mục được nhóm và không bao gồm.


Cảm ơn rất nhiều cho câu trả lời sâu sắc này. Xin lỗi, phải mất một tuần để trả lời - Tôi đã làm việc này mỗi ngày xen kẽ với công việc khác. Tôi đã triển khai các đề xuất của bạn cụ thể hóa việc tham gia TableA vào #AnotherTempTable. Điều này dường như có tác động tốt nhất - các đề xuất khác (sử dụng bảng tạm thời thay vì biến bảng cho @MyTableVar và sử dụng OPTION (RECOMPILE)không có tác dụng gì cả. 'Ẩn danh' và 'Đăng lên SQLPerformance.com' các tùy chọn trong SQL Sentry Plan Explorer rất tuyệt - Tôi vừa mới sử dụng chúng: answer.sqlperformance.com/questions/1087
Neo

-6

Tôi nhận thấy cần có PK trên @MyTableVar và đồng ý rằng #MyTableVar thường hoạt động tốt hơn (đặc biệt với số lượng hàng lớn hơn).

Điều kiện trong mệnh đề where

   WHERE A.A_Var8_FK_LK = @Param1

nên được chuyển đến tham gia bên trong A AND'ed. Trình tối ưu hóa không đủ thông minh theo kinh nghiệm của tôi để làm điều này (xin lỗi đã không nhìn vào kế hoạch) và nó có thể tạo ra sự khác biệt lớn.

Nếu những thay đổi đó không cho thấy sự cải thiện, tiếp theo tôi sẽ tạo một bảng tạm thời khác của A và tất cả những thứ mà nó tham gia bị ràng buộc (độc đáo?) Bởi A.A_Var8_FK_LK = @ Param1 nếu việc phân nhóm đó có ý nghĩa logic với bạn.

Sau đó tạo một chỉ mục được nhóm trên bảng tạm thời đó (trước hoặc sau khi tạo) cho điều kiện nối tiếp theo.

Sau đó nối kết quả đó với một vài bảng (F và T) còn lại.

Bam, người cần một kế hoạch truy vấn hôi thối khi ước tính hàng bị tắt và đôi khi không dễ dàng có thể ứng biến được ). Tôi giả sử bạn có các chỉ số thích hợp, đó là điều đầu tiên tôi sẽ kiểm tra trong kế hoạch.

Một dấu vết có thể cho thấy sự cố tràn tempdb có thể có hoặc không có tác động mạnh mẽ.

Một cách tiếp cận khác - ít nhất là thử nhanh hơn - là sắp xếp các bảng từ số hàng thấp nhất (A) đến cao nhất và sau đó bắt đầu thêm hợp nhất, băm và lặp vào các phép nối. Khi có gợi ý, thứ tự tham gia được cố định theo quy định. Những người dùng khác khôn ngoan tránh cách tiếp cận này vì nó có thể gây tổn hại trong thời gian dài nếu số lượng hàng tương đối thay đổi đáng kể. Một số gợi ý tối thiểu là mong muốn.

Nếu bạn đang làm nhiều trong số này, có lẽ một trình tối ưu hóa thương mại đáng để thử (hoặc dùng thử) và vẫn là một trải nghiệm học tập tốt.


Vâng, đúng vậy. Nó đảm bảo các hàng được trả về bởi A được giới hạn bởi các ràng buộc. Nếu không, trình tối ưu hóa có thể tham gia trước và áp dụng các ràng buộc sau. Tôi đối phó với điều này hàng ngày.
crokusek

4
@crokusek Bạn sai rồi. Trình tối ưu hóa của SQL-Server khá tốt khi biết các truy vấn là tương đương (cho dù điều kiện nằm ở mệnh đề WHERE hay ON) khi tham gia INNER.
ypercubeᵀᴹ

6
Bạn có thể thấy loạt bài của Paul White trên Trình tối ưu hóa truy vấn hữu ích.
Martin Smith

Đó là một thói quen khủng khiếp. Có thể nó sẽ cho trường hợp cụ thể này (trong đó có một ràng buộc) nhưng tôi đến từ vùng đất của nhiều nhà phát triển chồng chất lên các điều kiện AND tại mệnh đề where. SQL Server thực hiện không nhất quán "di chuyển" chúng sao cho join cho bạn.
crokusek

Đồng ý không chính xác cho bên ngoài (và tham gia bên phải). Nhưng khi chỉ có các biểu thức AND'd trong mệnh đề where và mỗi thuật ngữ chỉ tương ứng với một phép nối bên trong cụ thể, thuật ngữ đó có thể được chuyển một cách an toàn và tự tin đến vị trí "bật" như một cách tối ưu hóa và thực hành tốt nhất (imo). Cho dù đó là một điều kiện tham gia "đúng" hay chỉ là một ràng buộc cố định là thứ yếu để đạt được hiệu suất lớn. Liên kết đó là cho một trường hợp tầm thường. Cuộc sống thực có nhiều điều kiện với toán học () và toán học và do đó làm cho họ trở thành những ứng cử viên tốt hơn để từ đó rút ra những thực tiễn tốt nhất.
crokusek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.