Một số truy vấn không gian địa lý của SQL Server mất nhiều thời gian hơn nhiều so với các truy vấn khác


7

Chúng tôi có một bảng SQL Server đơn giản với dữ liệu không gian địa lý trông như thế này:

CREATE TABLE [dbo].[Factors](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [StateCode] [nvarchar](2) NOT NULL,
    [GeoLocation] [geography] NULL,
    [Factor] [decimal](18, 6) NOT NULL,
 CONSTRAINT [PK_dbo.Factors] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)

Hiện tại chúng tôi có khoảng 100 nghìn hàng trong đó, nhưng dự kiến ​​sẽ tăng lên hàng triệu.

Chúng tôi chạy các truy vấn trên đó trông như thế này:

declare @state nvarchar(2) = 'AL'
declare @point geography = geography::STGeomFromText('POINT(-86.19146040 32.38225770)', 4326)

select top 3 
    Lat,
    Lon,
    Factor, 
    GeoLocation.STDistance(@point) as Distance
from dbo.Factors
where StateCode = @state and GeoLocation.STDistance(@point) is not null
order by Distance

Đây là một chút đó là lạ. Dữ liệu trong bảng đó là không chính xác: chúng tôi đã lấy nó cho các phần phía nam của một tiểu bang, nhưng không phải cho toàn bộ tiểu bang. Nếu điểm chúng tôi tìm kiếm nằm trong phạm vi vài trăm mét mà chúng tôi có dữ liệu (ví dụ: từ phần phía nam của tiểu bang), truy vấn sẽ trả về giây. Nhưng nếu đó là, ví dụ, cách điểm dữ liệu gần nhất 100 km (ví dụ: nếu điểm mục tiêu là từ phía bắc của tiểu bang), thì truy vấn sẽ mất tối đa 3 phút để quay lại. Trong cả hai trường hợp, các kế hoạch truy vấn chỉ ra rằng chúng bắt đầu bằng việc quét chỉ mục không gian địa lý, do đó, đôi khi không phải là vấn đề xảy ra, SQL Server không thể hiểu được nên sử dụng chỉ mục nào trong câu hỏi.

Giả định của tôi là nó có liên quan đến cách chỉ số không gian địa lý được đặt ra.

CREATE SPATIAL INDEX IX_Factors_Spatial 
ON [dbo].[Factors] (GeoLocation)
USING GEOGRAPHY_AUTO_GRID 
    WITH (
            CELLS_PER_OBJECT = 16, 
            PAD_INDEX = OFF, 
            STATISTICS_NORECOMPUTE = OFF, 
            SORT_IN_TEMPDB = OFF, 
            DROP_EXISTING = OFF, 
            ONLINE = OFF, 
            ALLOW_ROW_LOCKS = ON, 
            ALLOW_PAGE_LOCKS = ON);

Nhưng tôi không biết rằng tôi đã mò mẫm các chi tiết đủ tốt để đặt ngón tay vào vấn đề.

Bất kỳ đề xuất cho cách tiếp cận xử lý sự cố này?


2
Bạn có luôn sử dụng các biến cục bộ hay bạn gọi một thủ tục được lưu trữ trong đó các biến đó được truyền vào?
Erik Darling

Ngoài ra làm thế nào bạn đang cố gắng để chèn? Bạn đang làm điều này thông qua SSMS, hoặc một số loại ứng dụng?
Austin

Ngoài ra, có thể là một ý tưởng tốt để chạy các tính toán này trong một chương trình hơn là trong cơ sở dữ liệu.
Austin

1
@Austin - Không chắc chắn về câu hỏi đầu tiên của bạn. Chúng tôi đang cố đọc dữ liệu, không chèn dữ liệu. (Khi chúng tôi chèn nó, trong quá trình gieo hạt của chúng tôi, chúng tôi sẽ chèn nó bằng Entity Framework, đã đọc dữ liệu từ tệp CSV cơ sở.) Và khi chúng hoạt động, chúng hoạt động thực sự khá tốt, sử dụng ít bộ nhớ hơn và nhanh hơn tôi có thể viết mã bằng tay, tôi khá chắc chắn.
Ken Smith

2
@KenSmith - Tôi nghĩ rằng Erik đang cố gắng hiểu nếu có một kế hoạch xấu có khả năng được tạo ra đang gây ra các truy vấn chậm. Nếu bạn có thể thực hiện các thử nghiệm trên một hệ thống phi sản xuất, hãy thử dbcc freeproccachesau đó chạy truy vấn cho phần phía bắc của trạng thái để xem nó có nhanh không.
Max Vernon

Câu trả lời:


6

Câu trả lời ngắn: So sánh các kế hoạch thực hiện thực tế cho các biến thể nhanh và chậm và bạn sẽ thấy chính mình.

Khi điểm đã cho @pointgần với các điểm trong bảng, các phép tính được sử dụng trong chỉ mục không gian thực sự giúp loại bỏ hầu hết các hàng và chỉ cần vài lần tìm kiếm của chỉ mục.

Khi đã cho @pointlà xa bất kỳ điểm nào trong bảng, động cơ có hiệu quả phải đọc tất cả các hàng. Nó tìm kiếm một chỉ số 100 nghìn lần, chậm.

Nếu bạn vô hiệu hóa chỉ mục không gian, bạn sẽ thấy hiệu suất của truy vấn trở nên giống nhau cho bất kỳ @point. Nó sẽ chậm hơn biến thể nhanh của bạn khi chỉ mục hữu ích, nhưng nó sẽ nhanh hơn biến thể chậm của bạn khi chỉ mục có hại.

Xem Tổng quan về chỉ mục không gian nếu bạn chưa biết chi tiết cơ bản về cấu trúc bên trong của chỉ mục đó.

Dữ liệu mẫu thử

CREATE TABLE [dbo].[Factors](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [GeoLocation] [geography] NULL,
    [Factor] [decimal](18, 6) NOT NULL,
 CONSTRAINT [PK_Factors] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

Tạo ~ 100K hàng trong phạm vi ~ 20km bằng ~ 20km.

DECLARE @MinLat float = -38.180184;
DECLARE @MaxLat float = -38.000000;
DECLARE @MinLon float = 145.000000;
DECLARE @MaxLon float = 145.227707;
DECLARE @PointCount int = 317;

WITH 
x AS
(
    SELECT TOP (@PointCount)
        ROW_NUMBER() OVER (ORDER BY [object_id]) AS rn
    FROM sys.all_objects
)
INSERT INTO [dbo].[Factors]
    ([GeoLocation]
    ,[Factor])
SELECT
    geography::Point(
         @MinLat + (TLat.rn-1) * (@MaxLat - @MinLat) / (@PointCount-1)
        ,@MinLon + (TLon.rn-1) * (@MaxLon - @MinLon) / (@PointCount-1)
        ,4326) AS GeoLocation
    ,0 AS Factor
FROM
    x AS TLat CROSS JOIN x AS TLon
ORDER BY TLat.rn, TLon.rn;

Tạo chỉ mục không gian mặc định

CREATE SPATIAL INDEX [IX_GeoLocation] ON [dbo].[Factors]
(
    [GeoLocation]
)USING  GEOGRAPHY_AUTO_GRID 
WITH (
CELLS_PER_OBJECT = 16, 
PAD_INDEX = OFF, 
STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, 
ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Kiểm tra truy vấn

@point1gần với các điểm khác trong bảng. @point2là xa các điểm khác trong bảng.

declare @point1 geography = geography::Point(-38.000000, 145.000000, 4326);
declare @point2 geography = geography::Point(+38.000000, 145.000000, 4326);

select top 3 
    Factor, 
    GeoLocation.STDistance(@point1) as Distance
from dbo.Factors
where GeoLocation.STDistance(@point1) is not null
order by Distance
option(recompile);


select top 3 
    Factor, 
    GeoLocation.STDistance(@point2) as Distance
from dbo.Factors
where GeoLocation.STDistance(@point2) is not null
order by Distance
option(recompile);

Kế hoạch thực hiện và IO

Chỉ mục được kích hoạt

IO. Kết quả hàng đầu là nhanh (7ms, 171 lần đọc). Kết quả dưới cùng là chậm (5.693ms, 234.662 lần đọc).

chỉ số kích hoạt IO

Nhanh.

chỉ số kích hoạt nhanh

Chậm.

chỉ số kích hoạt chậm

Chỉ mục bị vô hiệu hóa

IO. Cả hai truy vấn có cùng số lần đọc (601) và cùng thời lượng (~ 1700ms).

chỉ số bị vô hiệu hóa IO

Gói giống nhau cho cả hai truy vấn:

kế hoạch vô hiệu hóa

Nó nhanh hơn để quét hàng 100K, hơn là tìm kiếm 100K lần.


Tôi không biết làm thế nào để giải quyết vấn đề, nếu có cách nào để tận dụng tốt nhất cả hai thế giới và bằng cách nào đó tự động quyết định có nên sử dụng chỉ mục hay không.

Bạn có thể thử tính hộp giới hạn (min / max lat / lon) và thay đổi logic dựa trên việc điểm đã cho có nằm trong hộp giới hạn hay không.

Điều thú vị nhất xảy ra trong chức năng có giá trị bảng Geodetic T shipation tích hợp đó và tôi không thấy cách điều chỉnh nó.

Với các chỉ mục không gian rất nhiều phụ thuộc vào phân phối dữ liệu của bạn.

Trong một số trường hợp, bạn có thể tốt hơn với hai chỉ số tiêu chuẩn đơn giản riêng biệt về vĩ độ và kinh độ nếu bạn biết rằng dữ liệu của mình dày đặc và bạn có thể giới hạn tìm kiếm trong một dải hẹp hoặc diện tích nhỏ (điểm đã cho + - vài km).


Đây là một câu trả lời rất chi tiết và nó đánh tôi là chính xác, ngay cả khi tôi không thích nó :-). Tôi sẽ điều tra một số giải pháp khác nhau, bao gồm chỉ điền dữ liệu (với hệ số -1 hoặc tương tự) ở mức độ phân giải thấp cho các điểm mà chúng tôi không có dữ liệu thực.
Ken 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.