Wie verwende ich Indizes mit innerem Join in Postgis?

8

Ich habe 2 Sätze von Punkten in 2 separaten Tabellen. Table_a hat 100.000 Punkte und table_b hat 300.000 Punkte. Wenn ich versuche, die nächsten Punkte in Bezug zu finden, finde ich einen beliebigen Punkt aus table_b, der sich innerhalb von 50 Metern von tabla_a befindet. Nachdem Sie die Fallspalte berechnet haben, gruppieren Sie sie nach der Spalte table_a a_id und geben den höchsten Wert zurück.

Ich habe eine folgende Abfrage geschrieben, die diese Kriterien erfüllt

SELECT DISTINCT ON (a_id) *
FROM (
       SELECT
         table_b.b_id,
         table_b.height - st_3ddistance(table_b.geom, table_a.geom) fall,
         table_b.geom,
         table_a.a_id
       FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;

Ich habe 3D-Geometrieindizes hinzugefügt:

CREATE INDEX table_a_geom ON table_a USING GIST (geom gist_geometry_ops_nd);
CREATE INDEX table_b_geom ON table_b USING GIST (geom gist_geometry_ops_nd);

Mein Problem ist jedoch, dass ich keine Abfrage machen kann, um sie zu verwenden. Der Abfrageplaner wählt weiterhin einen langsamen Sequenzscan. Ich führe einen Test durch, bei dem _st_3ddwithin mit st_3ddwithin , <<- >> <50 geändert wird , wobei 50 m Puffer erstellt und st_3ddistance <50 geschnitten werden , aber jedes Mal, wenn der Planer den Sequenzscan wählt. Gibt es eine Möglichkeit, Indizes mit höherer Leistung zu verwenden oder die Abfrage so zu ändern, dass Indizes verwendet werden?

Mein Abfrageplan:

Unique  (cost=10462593.70..10473018.43 rows=1 width=144)
  ->  Sort  (cost=10462593.70..10467806.06 rows=2084945 width=144)
        Sort Key: table_a.nmbayuid, ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom))) DESC
        ->  Nested Loop  (cost=0.00..10243762.28 rows=2084945 width=144)
              Join Filter: (_st_dwithin(table_a.geom, table_b.geomgr, '50'::double precision) AND ((table_b.height - st_3ddistance(table_b.geomgr, table_a.geom)) >= '0'::double precision))
              ->  Seq Scan on table_b  (cost=0.00..1459.47 rows=47147 width=96)
              ->  Materialize  (cost=0.00..10.97 rows=398 width=56)
                    ->  Seq Scan on table_a  (cost=0.00..8.98 rows=398 width=56)
Losbaltica
quelle
1
Was genau ist e_wires_mv12404 im Abfrageplan, aber nicht im SQL? Wie sieht der Abfrageplan nur für die innere Abfrage aus? Ich schlage vor, keine Funktion zu verwenden, die mit _ST beginnt. Schließlich können Sie mit ST_DWithin in 2D möglicherweise eine bessere Leistung erzielen, wenn Sie 35 Meter verwenden, was mehr oder weniger 50 Metern von gegenüberliegenden Kanten eines Würfels entspricht. Da Sie innerhalb von 50 Metern nach dem nächstgelegenen Punkt suchen, ist dies möglicherweise ein guter Kandidat für eine seitliche Verbindung und die Verwendung des Konstrukts ORDER BY a.geom <-> b.geom.
John Powell
1
Ich hatte letztes Jahr ein ähnliches Problem. Ich habe diesen Beitrag für Sie ausgegraben. Lassen Sie mich wissen, wenn er Ihre Fragen nicht beantwortet.
WxGeo
2
Wenn Sie sich die SQL-Definition der Funktionen ansehen, sehen Sie, dass die st_-Funktionen wie st_dwithin tatsächlich ein Begrenzungsrahmen-Check und ein Aufruf der st- Funktion sind. Es ist der Begrenzungsrahmen, der den Index verwenden kann, wenn Sie die st- Funktion direkt aufrufen . Die Datenbank kann den Index nicht verwenden. Sie rufen die Überprüfungsfunktion direkt auf.
Nicklas Avén
1
Möchten Sie, dass ich die Lateral Join-Lösung aufschreibe? Ich denke, sie würde gut für das funktionieren, was Sie beschreiben
John Powell
1
@ AndSilva-Funktionen, die mit beginnen, _STsind interne Funktionen, die von PostGIS nach dem Filtern mit einem Index aufgerufen werden. Wenn Sie sie direkt aufrufen, wird der Index nicht verwendet.
Dbaston

Antworten:

6

Erstens führt, wie in den Kommentaren erwähnt, der führende Unterstrich vor der ST-Funktion, dh _ST_3DWithin, dazu, dass der Index nicht verwendet wird. Ich kann dies in letzter Zeit nicht erwähnen, aber in älteren Dokumenten heißt es, wenn Sie nach z. B. _ST_Intersects suchen:

Verwenden Sie die Funktion _ST_Intersects, um die Verwendung von Indizes zu vermeiden.

BEARBEITEN: Wie von @dbaston in den Kommentaren klargestellt, sind die Funktionen mit dem führenden Unterstrich interne Funktionen, die den Index beim Aufruf nicht verwenden, und dies ist weiterhin der Fall (obwohl es in den Dokumenten schwer zu finden ist).

Ihre Abfrage könnte möglicherweise von der LATERAL JOIN-Syntax profitieren, die sich gut für Probleme mit k nächsten Nachbarn (kNN) wie diesem eignet.

SELECT 
   a.a_id, 
   b.b_id
   b.height - ST_3Ddistance(b.geom, a.geom) AS fall,
  FROM table_a a
     LEFT JOIN LATERAL
       (SELECT
            b_id,         
            geom,
            height        
          FROM table_b
          WHERE ST_3Ddwithin(a.geom, geom, 50)
          AND height - ST_3Ddistance(geom, a.geom) > 0
          ORDER BY height - ST_3Ddistance(b.geom, a.geom) DESC 
          LIMIT 1
        ) b ON TRUE;

Auf diese Weise können Sie die nächsten k Geometrien von Tabelle a (in diesem Fall 1 aufgrund von LIMIT 1) bis Tabelle b finden, geordnet nach dem 3D-Abstand zwischen ihnen. Es wird mit LEFT JOIN geschrieben, da es denkbar ist, dass in Tabelle a einige Geometrien vorhanden sind, die nicht innerhalb von 50 Metern von Tabelle b liegen.

Mit den seitlichen Abfragen können Sie auf Spalten aus der vorherigen FROM-Klausel verweisen. Dies macht sie leistungsfähiger als Standard-Unterabfragen (siehe Dokumente) .

Ich kann dies nicht mit Ihren Daten testen, aber wenn ich ähnliche Abfragen ausgeführt habe, zeigt die EXPLAIN-Anweisung die ordnungsgemäße Verwendung des Index an.

John Powell
quelle
Ihre Kommentare sind sehr sinnvoll, aber ich kann die Antwort nicht akzeptieren, da die von Ihnen angegebene Abfrage anders funktioniert als die ursprüngliche Abfrage. Wie ich zuvor sagte "Ich suche nicht nach einem einzigen nächstgelegenen Punkt, sondern nach einer Gruppe von Punkten innerhalb von 50 Metern und wähle dann einen mit dem höchsten Subtraktionswert (Höhe - ST_3Ddistance (geom, a.geom)), gruppiert nach a_id
Losbaltica
Ich habe Ihre Anfrage geändert, bitte schauen Sie nach und fügen Sie bei Bedarf Verbesserungen hinzu :)
Losbaltica
1
Ich habe die Abfrage geändert, es fehlte nur noch "height -" in der Reihenfolge von. Dadurch werden nun alle Punkte innerhalb von 50 gefunden und der Punkt mit der höchsten Höhe zurückgegeben - ST_3Ddistance (b.geom, a.geom). Es ist keine eindeutige Einstellung erforderlich, da dies alles von jeder seitlichen Abfrage und LIMIT 1 behandelt wird, dh Sie erhalten nur den größten Fallwert für jede a_id.
John Powell
Funktioniert das jetzt so, wie Sie es ursprünglich erwartet hatten? Sieht EXPLAIN sinnvoll aus?
John Powell
Funktioniert wie erwartet. Die Abfrageleistung ist fast gleich, aber die Kosten für die Abfrage sind viel geringer. Neue Erklärung: EXPLAIN.depesz.com/s/Js5G Ich glaube, ich bin an der Grenze der Abfrageoptimierung angelangt und denke nur, dass ich jetzt den Server optimieren oder die Tabellen / Logik umgestalten kann. Also beantwortet es mir die ursprüngliche Frage
Losbaltica
2

Dieser Link zur PostGIS-Dokumentation empfiehlt die folgenden Schritte, um sicherzustellen, dass Indizes und Abfrageplaner optimiert sind:

  1. Stellen Sie sicher, dass Statistiken über die Anzahl und Verteilung der Werte in einer Tabelle gesammelt werden, um dem Abfrageplaner bessere Informationen für Entscheidungen zur Indexnutzung zu liefern. VACUUM ANALYZE berechnet beide.

  2. Wenn das Staubsaugen nicht hilft, können Sie den Planer vorübergehend zwingen, die Indexinformationen zu verwenden, indem Sie den Wert enable_seqscan auf off setzen. Befehl. Auf diese Weise können Sie überprüfen, ob der Planer überhaupt in der Lage ist, einen indexbeschleunigten Abfrageplan für Ihre Abfrage zu generieren. Sie sollten diesen Befehl nur zum Debuggen verwenden: Im Allgemeinen weiß der Planer besser als Sie, wann Indizes verwendet werden sollen. Vergessen Sie nach dem Ausführen Ihrer Abfrage nicht, ENABLE_SEQSCAN wieder zu aktivieren, damit andere Abfragen den Planer wie gewohnt verwenden.

  3. Wenn enable_seqscan auf off gesetzt ist; Wenn Ihre Abfrage ausgeführt wird, ist Ihr Postgres wahrscheinlich nicht auf Ihre Hardware abgestimmt. Wenn Sie feststellen, dass der Planer hinsichtlich der Kosten für sequentielle und Index-Scans falsch ist, reduzieren Sie den Wert von random_page_cost in postgresql.conf oder setzen Sie random_page_cost auf 1.1;. Der Standardwert für den Parameter ist 4, versuchen Sie, ihn auf 1 (auf SSD) oder 2 (auf schnellen Magnetplatten) einzustellen. Durch Verringern des Werts ist der Planer eher geneigt, Index-Scans zu verwenden.

  4. Wenn enable_seqscan auf off gesetzt ist; hilft Ihrer Abfrage nicht, es kann vorkommen, dass Sie eine Konstruktion verwenden, die Postgres noch nicht entwirren kann. Eine Unterabfrage mit Inline-Auswahl ist ein Beispiel - Sie müssen sie in den Formularplaner umschreiben, um beispielsweise einen LATERAL JOIN zu optimieren.

Versuchen Sie also zuerst die Schritte 1 bis 3, bevor Sie Ihre Abfrage neu schreiben, um die Indizes zu verwenden. Wenn dies nicht funktioniert, können Sie versuchen, die Abfrage zu ändern.

Ich glaube (nach bestem Wissen und Gewissen, SQL zu erstellen, ohne den Code auszuführen), dass die folgende Abfrage identische Ergebnisse für Ihre zurückgibt, weiß aber nicht, ob sie effizienter ist.

SELECT DISTINCT on (a_id),
    table_b.b_id as b_id,
    table_b.height - st_3ddistance(table_b.geom, table_a.geom) as fall,
    table_b.geom as b_geom,
    table_a.a_id as a_id
    FROM table_a
         INNER JOIN table_b ON _st_3ddwithin(table_a.geom, table_b.geom, 50)) a
WHERE fall >= 0
ORDER BY a_id, fall DESC;
jgm_GIS
quelle
Sehr interessant, nachdem _st_3ddwithin in st_dwithin geändert wurde, wie in anderen Kommentaren vorgeschlagen, und anschließend VACUUM ANALYZE ausgeführt wird, beginnt der Planer endlich, den Index abzufangen!
Losbaltica
0

Wenn Sie Postgres 10 (oder neuer) verwenden, würde ich dringend empfehlen, Ihre Daten in parallele Tabellen zu laden.

Sie müssen wahrscheinlich Zeit damit verbringen, es zu optimieren (Datenpartitionierung und Anzahl der Mitarbeiter), aber ich denke, die Mühe lohnt sich. Theoretisch ist KNN stark parallelisierbar und erreicht konstante Zeitkomplexitäten, sogar O (1), wenn die Anzahl der Arbeiter gleich der Anzahl der Elemente ist, für die eine KNN-Operation berechnet wird.

Einige praktische Bezug auf die Daten geladen werden und die Durchführung der Abfragen werden zur Verfügung gestellt hier . Er bietet einige Details über Plan tunning (um mehr Arbeiter zu zwingen, werden actioned) hier . Es ist wichtig zu bemerken, dass parallele Skripte viel Aufgabenkoordination erfordern, so dass die extreme theoretische Grenze für die Bereitstellung der extremsten Parallelisierung in der Praxis aufgrund der Vernetzung und anderer Systemdesignmerkmale nicht gilt.

Ricardo Barros Lourenço
quelle