Verbessern einer DbGeography-Abfrage

7

Ich bin noch neu in der Datenbankverwaltung und versuche, eine Suchabfrage zu optimieren.

Ich hatte eine Abfrage, die so aussah und in einigen Fällen 5 bis 15 Sekunden für die Ausführung benötigte. Außerdem verursachte sie eine 100% ige CPU-Auslastung:

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT(3.3109015 6.648294)', 4326); 

SELECT TOP (1)
     [Result].[PointId] AS [PointId], 
     [Result].[PointName] AS [PointName], 
     [Result].[LegendTypeId] AS [LegendTypeId], 
     [Result].[GeoPoint] AS [GeoPoint]
FROM ( 
    SELECT 
        [Extent1].[GeoPoint].STDistance(@point) AS distance, 
        [Extent1].[PointId] AS [PointId], 
        [Extent1].[PointName] AS [PointName], 
        [Extent1].[LegendTypeId] AS [LegendTypeId], 
        [Extent1].[GeoPoint] AS [GeoPoint]
    FROM [dbo].[GeographyPoint] AS [Extent1]
    WHERE 18 = [Extent1].[LegendTypeId] 
)  AS [Result]
ORDER By [Result].distance ASC

Diese Tabelle enthält einen Clustered-Index für die PK und einen räumlichen Index für die geographyTypspalte.

Geben Sie hier die Bildbeschreibung ein

Als ich die obige Abfrage ausführte, führte sie einen Scanvorgang durch.

Geben Sie hier die Bildbeschreibung ein

Also habe ich einen nicht gruppierten Index für die LegendTypeIdSpalte erstellt:

CREATE NONCLUSTERED INDEX [GeographyPoint_LegendType_NonClustered] ON [dbo].[GeographyPoint]
(
    [LegendTypeId] ASC
)
INCLUDE (   [PointId],
    [PointName],
    [GeoPoint]) 
    WITH (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]
GO

und änderte die Abfrage in:

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT({0} {1})', 4326); 

 SELECT TOP (1) 
     [GeoPoint].STDistance(@point) AS distance, 
     [PointId], 
     [PointName],
     [LegendTypeId], 
     [GeoPoint]
     FROM [GeographyPoint]
 WHERE 18 = [LegendTypeId]
 ORDER By distance ASC

Und jetzt führt SQL Server eine Suche anstelle des Scans durch:

Geben Sie hier die Bildbeschreibung ein

Dies hat meiner Ansicht nach die Effizienz der Abfrage erhöht, aber wenn ich diese für die Produktion bereitstelle, erhalte ich immer noch die gleichen Ergebnisse (hohe CPU-Auslastung und durchschnittlich 10 Sekunden für die Ausführung der Abfrage).

Hinweis: In diese Tabelle werden keine Daten eingefügt, aktualisiert oder entfernt - nur Suchen / Lesen.

  1. Ist es etwas, was ich falsch mache?

  2. Wie kann ich das beheben?

BEARBEITEN

Index Seak Details

Geben Sie hier die Bildbeschreibung ein

EDIT 2:

Ich habe die Abfrage geändert, um die Methode "Nächster Nachbar" vom Link: https://msdn.microsoft.com/en-us/library/ff929109.aspx zu verwenden . Dies ist nun das Ergebnis. Diese Abfrage benötigt ebenfalls 3 -5 Sekunden für die Suche - ähnlich wie bei der zweiten Abfrage (jedoch nicht in der Produktion getestet)

Geben Sie hier die Bildbeschreibung ein

Raumindexeinstellungen:

CREATE SPATIAL INDEX [SPATIAL_Point] ON [dbo].[GeographyPoint]
(
[GeoPoint]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = MEDIUM,LEVEL_2 = MEDIUM,LEVEL_3 = MEDIUM,LEVEL_4 = MEDIUM), 
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]
GO

BEARBEITEN 3 Ich habe die Anweisungen von @MickyT befolgt, den Index gelöscht [LegendTypeId]und die folgende Abfrage ausgeführt:

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT(3.3109 6.6482)', 4326); 

SELECT TOP (1) 

    [PointId],
    [PointName],
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint] WITH(INDEX(SPATIAL_Point))
WHERE 
   [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)

Statistiken für diese Abfrage sind

Geben Sie hier die Bildbeschreibung ein

Und dann habe ich diese Abfrage erneut ausgeführt:

DECLARE @point geography;
SET @point = geography::STPointFromText('POINT(3.3109 6.6482)', 4326); 

 SELECT TOP (1) 
     [GeoPoint].STDistance(@point) AS distance, 
     [PointId], 
     [PointName],
     [LegendTypeId], 
     [GeoPoint]
     FROM [GeographyPoint] --WITH(INDEX(SPATIAL_Point))
 WHERE 18 = [LegendTypeId]
 ORDER By distance ASC

Statistiken für diese Abfrage sind

Geben Sie hier die Bildbeschreibung ein

Dawood Awan
quelle
Wie viele tatsächliche Zeilen werden von der Suche zurückgegeben? Beachten Sie, dass STDistance für jede zurückgegebene Zeile berechnet und das gesamte Ergebnis für die ORDER BY DistanceKlausel sortiert werden muss .
Dan Guzman
@ DanGuzman Ich habe die Frage mit den Suchdetails bearbeitet
Dawood Awan
1
Die Abfrage, die Sie betrachten, wird als Suche nach dem nächsten Nachbarn bezeichnet und ist recht häufig, aber teuer. Hier ist die MSDN-Seite , auf der die Anforderungen für eine effiziente Ausführung beschrieben werden. Dies war von 2012,
MickyT
@ MickyT dieser nächste Nachbar scheint die gleiche wie die zweite Abfrage durchzuführen
Dawood Awan
Ich würde erwarten, dass es den räumlichen Index erreicht. Ich werde es mir morgen genauer ansehen. Auf welcher SQL Server-Version befinden Sie sich und können Sie die Einstellung für Ihren räumlichen Index freigeben? Wie viele Punkte in Ihrer Tabelle?
MickyT

Antworten:

2

Ich habe das folgende Setup verwendet, um einige Tests durchzuführen.

CREATE TABLE GeographyPoint (
    ID INTEGER IDENTITY(1,1) NOT NULL PRIMARY KEY,
    GeoPoint GEOGRAPHY NOT NULL,
    LegendTypeID INTEGER NOT NULL
    );

INSERT INTO GeographyPoint (GeoPoint, LegendTypeID)
SELECT TOP 1000000 
    Geography::Point(RAND(CAST(NEWID() AS VARBINARY(MAX))) * 2,RAND(CAST(NEWID() AS VARBINARY(MAX))) * 2,4326),
    CAST(RAND(CAST(NEWID() AS VARBINARY(MAX))) * 25 AS INTEGER)
FROM Tally;

CREATE INDEX GP_IDX1 ON GeographyPoint(LegendTypeID) INCLUDE (ID, GeoPoint);
CREATE SPATIAL INDEX GP_SIDX ON GeographyPoint(GeoPoint) USING GEOGRAPHY_AUTO_GRID;

Dies ergibt eine Tabelle mit 1.000.000 zufälligen Punkten mit einer Streuung von 2 x 2 Grad.
Nachdem ich einige verschiedene Optionen ausprobiert hatte, war die beste Leistung, die ich erzielen konnte, die Verwendung des räumlichen Index zu erzwingen. Es gab verschiedene Möglichkeiten, dies zu erreichen. Löschen des Index auf LegendTypeID oder Verwenden eines Hinweises.
Sie müssen entscheiden, welches für Ihre Situation am besten geeignet ist. Persönlich verwende ich keine Indexhinweise und würde den anderen Index löschen, wenn er für andere Abfragen nicht benötigt wird.

Die Abfragen stapelten sich gegeneinander

DECLARE @point geography;
SET @point = geography::Point(1,1,4326); 
/*
Clustered index scan (PK)
 SQL Server Execution Times:
   CPU time = 641 ms,  elapsed time = 809 ms
*/
SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint]
WHERE 18 = [LegendTypeId]
ORDER By distance ASC
OPTION(MAXDOP 1)
/*
Index Seek NonClustered (GP_IDX1)
 SQL Server Execution Times:
   CPU time = 2250 ms,  elapsed time = 2806 ms
*/
SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint]
WHERE [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)

/*
For the next 2 queries
Clustered Index Seek (Spatial)
 SQL Server Execution Times:
   CPU time = 15 ms,  elapsed time = 11 ms
*/
SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint] WITH(INDEX(GP_SIDX))
WHERE [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)

DROP INDEX GP_IDX1 ON [GeographyPoint]

SELECT TOP (1) 
    [GeoPoint].STDistance(@point) AS distance, 
    [ID], 
    [LegendTypeId], 
    [GeoPoint]
FROM [GeographyPoint]
WHERE [GeoPoint].STDistance(@point) IS NOT NULL AND
    18 = [LegendTypeId]
ORDER By [GeoPoint].STDistance(@point) ASC
OPTION(MAXDOP 1)
MickyT
quelle
Sollte ich den Clustered-Index für die PK in meiner Tabelle entfernen?
Dawood Awan
@DawoodAwan Entfernen Sie Ihren Clustered-Index nicht, er ist für den räumlichen Index erforderlich. Derjenige, der den Optimierer veranlasst, unter Verwendung des räumlichen Index zu verwerfen, ist der nicht gruppierte Index auf LegendTypeID
MickyT
Ok, ich probiere es aus. Sind die Einstellungen in meinem Raumindex in Ordnung?
Dawood Awan
@DawoodAwan Sie sehen für mich in Ordnung aus, aber es hängt wirklich von den Daten ab, die in der Tabelle enthalten sind. Da es sich um Punkte handelt, können nicht viele Anpassungen vorgenommen werden.
MickyT
Ich habe die von Ihnen gemachten Vorschläge getestet - und die Ergebnisse zu der Frage unter Bearbeiten 3 hinzugefügt. Die Anzahl der logischen Lesevorgänge hat sich von 452 auf 4523 erhöht. Ist dies ein gutes Zeichen?
Dawood Awan