Tại sao bảng dẫn xuất này cải thiện hiệu suất?


18

Tôi có một truy vấn lấy một chuỗi json làm tham số. Json là một mảng các cặp kinh độ, vĩ độ. Một ví dụ đầu vào có thể là như sau.

declare @json nvarchar(max)= N'[[40.7592024,-73.9771259],[40.7126492,-74.0120867]
,[41.8662374,-87.6908788],[37.784873,-122.4056546]]';

Nó gọi một TVF tính toán số POI xung quanh một điểm địa lý, ở khoảng cách 1,3,5,10 dặm.

create or alter function [dbo].[fn_poi_in_dist](@geo geography)
returns table
with schemabinding as
return 
select count_1  = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 1,1,0e))
      ,count_3  = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 3,1,0e))
      ,count_5  = sum(iif(LatLong.STDistance(@geo) <= 1609.344e * 5,1,0e))
      ,count_10 = count(*)
from dbo.point_of_interest
where LatLong.STDistance(@geo) <= 1609.344e * 10

Mục đích của truy vấn json là gọi hàng loạt chức năng này. Nếu tôi gọi nó như thế này thì hiệu suất rất kém chỉ mất gần 10 giây cho chỉ 4 điểm:

select row=[key]
      ,count_1
      ,count_3
      ,count_5
      ,count_10
from openjson(@json)
cross apply dbo.fn_poi_in_dist(
            geography::Point(
                convert(float,json_value(value,'$[0]'))
               ,convert(float,json_value(value,'$[1]'))
               ,4326))

kế hoạch = https://www.brentozar.com/pastetheplan/?id=HJDCYd_o4

Tuy nhiên, việc di chuyển cấu trúc địa lý bên trong bảng dẫn xuất khiến hiệu suất được cải thiện đáng kể, hoàn thành truy vấn trong khoảng 1 giây.

select row=[key]
      ,count_1
      ,count_3
      ,count_5
      ,count_10
from (
select [key]
      ,geo = geography::Point(
                convert(float,json_value(value,'$[0]'))
               ,convert(float,json_value(value,'$[1]'))
               ,4326)
from openjson(@json)
) a
cross apply dbo.fn_poi_in_dist(geo)

kế hoạch = https://www.brentozar.com/pastetheplan/?id=HkSS5_OoE

Các kế hoạch trông gần như giống hệt nhau. Không sử dụng song song và cả hai đều sử dụng chỉ số không gian. Có một spool lười biếng bổ sung vào kế hoạch chậm mà tôi có thể loại bỏ với gợi ý option(no_performance_spool). Nhưng hiệu suất truy vấn không thay đổi. Nó vẫn còn chậm hơn nhiều.

Chạy cả hai với gợi ý được thêm vào trong một đợt sẽ cân nhắc cả hai truy vấn như nhau.

Phiên bản máy chủ Sql = Microsoft SQL Server 2016 (SP1-CU7-GDR) (KB4057119) - 13.0.4466.4 (X64)

Vì vậy, câu hỏi của tôi là tại sao điều này lại quan trọng? Làm thế nào tôi có thể biết khi nào tôi nên tính toán các giá trị bên trong một bảng dẫn xuất hay không?


1
"Cân" có nghĩa là% chi phí ước tính? Con số đó gần như vô nghĩa, đặc biệt là khi bạn mang UDF, JSON, CLR qua địa lý, v.v.
Aaron Bertrand

Tôi biết, nhưng nhìn vào số liệu thống kê IO chúng giống hệt nhau. Cả hai đều thực hiện các thao tác đọc logic 36.330 trên point_of_interestbảng, cả hai đều quét chỉ số 4602 lần và cả hai đều tạo ra một bàn làm việc và tệp công việc. Người ước tính tin rằng các kế hoạch này là giống hệt nhau nhưng hiệu suất nói khác.
Michael B

Có vẻ như CPU ​​thực sự là vấn đề ở đây, có thể là do những gì Martin chỉ ra, không phải I / O. Thật không may, chi phí ước tính dựa trên CPU và I / O kết hợp và không luôn phản ánh những gì xảy ra trong thực tế. Nếu bạn tạo các gói thực tế bằng SentryOne Plan Explorer ( Tôi làm việc ở đó, nhưng công cụ này không có chuỗi ), sau đó thay đổi chi phí thực tế chỉ cho CPU, bạn có thể nhận được các chỉ số tốt hơn về thời gian sử dụng CPU.
Aaron Bertrand

1
@MartinSmith Không phải mỗi nhà khai thác, không. Chúng tôi làm bề mặt những người ở cấp độ tuyên bố. Hiện tại chúng tôi vẫn dựa vào triển khai ban đầu từ DMV trước khi các số liệu bổ sung đó được thêm vào ở cấp thấp hơn. Và chúng tôi đã có một chút bận rộn để làm việc trên một cái gì đó khác mà bạn sẽ thấy sớm. :-)
Aaron Bertrand

1
PS Bạn có thể cải thiện hiệu suất nhiều hơn bằng cách thực hiện một hộp số học đơn giản trước khi thực hiện phép tính khoảng cách đường thẳng. Đó là, lọc đầu tiên cho những nơi mà giá trị |LatLong.Lat - @geo.Lat| + |LatLong.Long - @geo.Long| < ntrước khi bạn làm phức tạp hơn sqrt((LatLong.Lat - @geo.Lat)^2 + (LatLong.Long - @geo.Long)^2). Và thậm chí tốt hơn, tính toán giới hạn trên và dưới trước, sau đó LatLong.Lat > @geoLatLowerBound && LatLong.Lat < @geoLatUpperBound && LatLong.Long > @geoLongLowerBound && LatLong.Long < @geoLongUpperBound. (Đây là mã giả, thích nghi thích hợp.)
ErikE

Câu trả lời:


15

Tôi có thể cung cấp cho bạn câu trả lời một phần giải thích lý do tại sao bạn thấy sự khác biệt về hiệu suất - mặc dù điều đó vẫn để lại một số câu hỏi mở (chẳng hạn như SQL Server có thể tạo kế hoạch tối ưu hơn mà không đưa ra biểu thức bảng trung gian dự báo biểu thức dưới dạng cột không?)


Sự khác biệt là trong kế hoạch nhanh, công việc cần thiết để phân tích các phần tử mảng JSON và tạo Địa lý được thực hiện 4 lần (một lần cho mỗi hàng phát ra từ openjsonhàm) - trong khi nó được thực hiện hơn 100.000 lần so với kế hoạch chậm.

Trong kế hoạch nhanh ...

geography::Point(
                convert(float,json_value(value,'$[0]'))
               ,convert(float,json_value(value,'$[1]'))
               ,4326)

Được gán Expr1000trong vô hướng tính toán ở bên trái của openjsonhàm. Điều này tương ứng với geotrong định nghĩa bảng dẫn xuất của bạn.

nhập mô tả hình ảnh ở đây

Trong kế hoạch nhanh, bộ lọc và tham chiếu tổng hợp luồng Expr1000. Trong kế hoạch chậm, họ tham chiếu biểu thức cơ bản đầy đủ.

Luồng tính chất tổng hợp

nhập mô tả hình ảnh ở đây

Bộ lọc được thực thi 116.995 lần với mỗi lần thực hiện yêu cầu đánh giá biểu thức. Tập hợp luồng có 110.520 hàng chảy vào nó để tổng hợp và tạo ba tập hợp riêng biệt bằng biểu thức này. 110,520 * 3 + 116,995 = 448,555. Ngay cả khi mỗi đánh giá riêng lẻ mất 18 micro giây, điều này sẽ tăng thêm tối đa 8 giây cho toàn bộ truy vấn.

Bạn có thể thấy tác động của điều này trong thống kê thời gian thực tế trong kế hoạch XML (được chú thích bằng màu đỏ bên dưới từ kế hoạch chậm và màu xanh lam cho kế hoạch nhanh - thời gian tính bằng ms)

nhập mô tả hình ảnh ở đây

Tập hợp luồng có thời gian trôi qua cao hơn 6,209 giây so với con ngay lập tức của nó. Và phần lớn thời gian của trẻ đã được đưa lên bởi bộ lọc. Điều này tương ứng với các đánh giá biểu thức thêm.


Nhân tiện .... Nói chung, không có gì chắc chắn rằng các biểu thức cơ bản với các nhãn như Expr1000chỉ được tính một lần và không được đánh giá lại nhưng rõ ràng trong trường hợp này từ sự khác biệt về thời gian thực hiện điều này xảy ra ở đây.


Bên cạnh đó, nếu tôi chuyển đổi truy vấn sang sử dụng chéo áp dụng để tạo địa lý, tôi cũng nhận được kế hoạch nhanh. cross apply(select geo=geography::Point( convert(float,json_value(value,'$[0]')) ,convert(float,json_value(value,'$[1]')) ,4326))f
Michael B

Thật không may, nhưng tôi tự hỏi liệu có cách nào dễ dàng hơn để tạo ra kế hoạch nhanh không.
Michael B

Xin lỗi cho câu hỏi nghiệp dư, nhưng công cụ nào được hiển thị trong hình ảnh của bạn?
BlueRaja - Daniel Pflughoeft

1
@ BlueRaja-DannyPflughoeft đây là các kế hoạch thực hiện được hiển thị trong phòng quản lý (các biểu tượng được sử dụng trong SSMS đã được cập nhật trong các phiên bản gần đây nếu đó là lý do cho câu hỏi)
Martin Smith
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.