sử dụng hàm $ phân vùng để cải thiện hiệu năng truy vấn


7

Tôi có các bảng được phân vùng dựa trên một INTcột.

Tôi thấy một số truy vấn đang sử dụng $Partitionchức năng để so sánh số phân vùng thay vì so sánh dữ liệu thực tế.

Ví dụ thay vì nói:

select *
from T1 
    inner join T2 on T2.SnapshotKey = T1.SnapshotKey

chúng đã được viết như dưới đây:

select *
from T1 
    inner join T2 on $Partition.PF_Name(T2.SnapshotKey) = $Partition.PF_Name(T1.SnapshotKey)

nơi PF_Namelà tên của phân vùng chức năng.

Tôi thấy các nhận xét về các truy vấn đó đã được thực hiện để cải thiện hiệu suất và khi tôi chạy cả hai truy vấn, tôi thấy khác nhau về thời gian thực hiện và kế hoạch thực hiện khác nhau. Tôi không chắc hai truy vấn này khác nhau như thế nào.

Đây là một truy vấn thực sự:

-- this takes about 9 seconds 
select sum(PrinBal)
from fpc
    inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703

-- this takes about 5 seconds
select sum(PrinBal)
from fpc
    inner join gl on $Partition.SnapshotKeyPF(gl.SnapshotKey) = $Partition.SnapshotKeyPF(fpc.SnapshotKey)
where fpc.SnapshotKey = 201703

Và dưới đây là kế hoạch thực hiện của truy vấn thực sự:

Bạn có thể xem kế hoạch thực hiện ở đây

Xin lỗi, tôi không thể tải lên ngay cả một kế hoạch thực hiện vệ sinh vì các video tải lên được giám sát bởi mạng của chúng tôi và nó có thể là một vi phạm chính sách.

Câu hỏi là: Tại sao các kế hoạch thực hiện là khác nhau và tại sao truy vấn thứ hai nhanh hơn.

Tôi đánh giá cao nếu ai đó có thể chia sẻ bất kỳ ý tưởng về điều này. Chỉ quan tâm để biết tại sao họ khác nhau. Nhanh hơn là tốt hơn mặc dù.

Điều này đang xảy ra trên SQL Server 2014. Nếu tôi chạy tương tự trên SQL Server 2012, kết quả sẽ khác và truy vấn đầu tiên sẽ thực hiện nhanh hơn!

Câu trả lời:


9

Tại sao kế hoạch thực hiện là khác nhau

Truy vấn đầu tiên

select sum(PrinBal)
from fpc
    inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703

Trình tối ưu hóa biết:

  • gl.SnapshotKey = fpc.SnapshotKey; và
  • fpc.SnapshotKey = 201703

để nó có thể suy ra:

  • gl.SnapshotKey = 201703

Giống như bạn đã viết:

select sum(PrinBal)
from fpc
    inner join gl on gl.SnapshotKey = fpc.SnapshotKey
where fpc.SnapshotKey = 201703
and gl.SnapshotKey = 201703

Giá trị bằng chữ 201703 cũng có thể được sử dụng bởi trình tối ưu hóa để xác định id phân vùng. Với cả hai biến SnapshotKeyvị ngữ (một đã cho, một suy ra) điều này có nghĩa là trình tối ưu hóa biết id phân vùng cho cả hai bảng.

Đi xa hơn, với giá trị bằng chữ (201703) SnapshotKeyhiện có sẵn trên cả hai bảng, vị từ tham gia:

  • gl.SnapshotKey = fpc.SnapshotKey

đơn giản hóa để:

  • 201703 = 201703; hoặc đơn giản
  • true

Có nghĩa là không có vị ngữ tham gia nào cả. Kết quả là một tham gia chéo hợp lý. Thể hiện kế hoạch thực hiện cuối cùng bằng cú pháp T-SQL gần nhất có sẵn, như thể bạn đã viết:

SELECT
    CASE
        WHEN SUM(Q1.c) = 0 THEN NULL
        ELSE SUM(Q1.s)
    END
FROM 
(
    SELECT c = COUNT_BIG(*), s = SUM(GL.PrinBal)
    FROM dbo.gl AS GL
    WHERE GL.SnapshotKey = 201703
    AND $PARTITION.PF(GL.SnapshotKey) = $PARTITION.PF(201703)
) AS Q1
CROSS JOIN
(
    SELECT Dummy = 1
    FROM dbo.fpc AS FPC
    WHERE FPC.SnapshotKey = 201703
    AND $PARTITION.PF(FPC.SnapshotKey) = $PARTITION.PF(201703)
) AS Q2;

Truy vấn thứ hai

select sum(PrinBal)
from fpc
    inner join gl on $Partition.PF(gl.SnapshotKey) = $Partition.PF(fpc.SnapshotKey)
where fpc.SnapshotKey = 201703

Trình tối ưu hóa không còn có thể suy ra bất cứ điều gì về gl.SnapshotKey, vì vậy việc đơn giản hóa và biến đổi được thực hiện cho truy vấn đầu tiên không còn có thể.

Thật vậy, trừ khi đúng là mỗi phân vùng chỉ giữ một lần duy nhất SnapshotKey, việc viết lại không được đảm bảo để tạo ra kết quả tương tự.

Một lần nữa, biểu thị kế hoạch thực hiện được tạo bằng cú pháp T-SQL gần nhất có sẵn:

SELECT
    CASE
        WHEN SUM(Q2.c) = 0 THEN NULL
        ELSE SUM(Q2.s)
    END
FROM 
(
    SELECT
        Q1.PtnID,
        c = COUNT_BIG(*),
        s = SUM(Q1.PrinBal)
    FROM 
    (
        SELECT GL.PrinBal, PtnID = $PARTITION.PF(GL.SnapshotKey)
        FROM dbo.gl AS GL
    ) AS Q1
    GROUP BY
        Q1.PtnID
) AS Q2
CROSS APPLY
(
    SELECT Dummy = 1
    FROM dbo.fpc AS FPC
    WHERE
        $PARTITION.PF(FPC.SnapshotKey) = Q2.PtnID
        AND FPC.SnapshotKey = 201703
) AS Q3;

Lần này không có sự tham gia chéo hợp lý. Thay vào đó, có một phép nối tương quan (áp dụng) trên id phân vùng.


tại sao truy vấn thứ hai nhanh hơn

Điều này là khó để đánh giá từ các thông tin được đưa ra. Sử dụng dữ liệu giả và bảng dựa trên các truy vấn và hình ảnh kế hoạch được cung cấp, tôi thấy truy vấn đầu tiên vượt trội hơn lần thứ hai trong mọi trường hợp.

Cùng một truy vấn được thể hiện bằng cách sử dụng cú pháp khác nhau thường có thể tạo ra một kế hoạch thực hiện khác nhau, đơn giản là vì trình tối ưu hóa bắt đầu từ một điểm khác nhau và khám phá các tùy chọn theo một thứ tự khác trước khi tìm thấy một kế hoạch thực hiện phù hợp. Tìm kiếm kế hoạch không đầy đủ, và không phải mọi chuyển đổi logic có thể có sẵn, do đó kết quả cuối cùng có thể khác nhau. Như đã lưu ý ở trên, hai truy vấn không nhất thiết phải thể hiện cùng một yêu cầu (ít nhất là được cung cấp thông tin có sẵn cho trình tối ưu hóa).

Trên một lưu ý riêng, hãy lưu ý rằng việc triển khai cột lưu trữ ban đầu trong SQL Server 2012 (và ở mức độ thấp hơn một chút, 2014) có nhiều hạn chế, nhất là về mặt tối ưu hóa mọi thứ. Bạn có thể sẽ nhận được kết quả tốt hơn và nhất quán hơn bằng cách nâng cấp lên bản phát hành gần đây hơn (lý tưởng nhất là mới nhất). Điều này đặc biệt đúng nếu bạn đang sử dụng phân vùng.

Tôi chắc chắn sẽ không khuyên bạn nên tập thói quen viết lại các phép nối bằng cách sử dụng $PARTITION, ngoại trừ như là phương sách cuối cùng và với sự hiểu biết rất sâu sắc về những gì bạn đang làm.

Đó là tất cả những gì tôi có thể nói mà không thể xem chi tiết lược đồ hoặc kế hoạch.

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.