Limitare le righe tramite la funzione spaziale


9

Sto cercando di migliorare le prestazioni per la query di seguito. Indipendentemente da come scrivo la query (sottoquery nella clausola FROM, sottoquery nella clausola WHERE) postgres insiste sull'esecuzione di tutte le righe ~ 570K attraverso la costosa funzione ST_DWITHIN anche se ci sono solo 60 righe in cui county = 24. Come posso filtrare Postgres su county = 24 PRIMA di passare attraverso la funzione Postgis che mi sembra molto più veloce e molto più efficiente? 700ms non sono motivo di troppa preoccupazione, ma poiché questa tabella cresce a 10 M + sono preoccupato per le prestazioni.

Inoltre, p.id è una chiave primaria, p.zipcode è un indice fk, z.county è un indice fk e p.geom ha un indice GiST.

Query:

EXPLAIN ANALYZE
  SELECT count(p.id)
  FROM point AS p
  LEFT JOIN zipcode AS z
    ON p.zipcode = z.zipcode
  WHERE z.county = 24
    AND ST_DWithin(
      p.geom, 
      ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269), 
      16090.0,
      false
    )

SPIEGARE ANALISI:

Aggregate  (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
  ->  Hash Join  (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
        Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
        ->  Seq Scan on point p  (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
              Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
              Rows Removed by Filter: 557731
        ->  Hash  (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 3kB
              ->  Bitmap Heap Scan on zipcode z  (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
                    Recheck Cond: (county = 24)
                    Heap Blocks: exact=39
                    ->  Bitmap Index Scan on fki_zipcode_county_foreign_key  (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
                          Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms

Forse prova a cambiare la riga "punto come p sinistra unisci codice postale come z" a qualcosa come "punto come p sinistra unisci (SELEZIONA * DA codice postale DOVE zipcode.county = 24) come z"?
weiji14

Ho appena provato, stessi risultati. Quando copio le ~ 60 pointrighe in cui county = 24 su una nuova tabella da sole, la query richiede solo .453ms rispetto a 724, quindi c'è sicuramente una grande differenza.
Josh,

1
Dovresti usare count(*)come una questione di stile. Se idè un pkid come dici tu, NOT NULLsignifica che sono gli stessi. Tranne che count(id)ha lo svantaggio che devi porre quella domanda se idè nullable.
Evan Carroll,

1
Posso chiederti perché stai usando un join esterno sinistro? Prova a cambiarlo in un join interno ... I risultati dovrebbero essere identici
MickyT

Se z.country è il fattore limitante, ti suggerirei di inserirlo prima in una query CTE e quindi di controllare quei risultati per un'intersezione con il punto di interesse. Poiché in questo caso l'indice spaziale è probabilmente meno selettivo di county = 24, si sta solo mettendo in mezzo.
John Powell,

Risposte:


3

È possibile visualizzare il problema con i conteggi delle righe previsti e effettivi. Il pianificatore ritiene che ci siano 7.437 righe, tuttavia ci sono solo 63. Le statistiche sono disattivate. È anche interessante notare che non sta usando una ricerca indice (index) del riquadro di selezione con cui DWithinpuoi incollare il risultato \d point. Quale versione di PostGIS e PostgreSQL?

Prova a correre ANALYZE point. Ottieni lo stesso piano quando sposti la condizione?

JOIN zipcode AS z
  ON p.zipcode = z.zipcode
  AND z.county = 24

Ho eseguito l'analisi e ho anche provato la nuova condizione AND in ON, ma continuavo a ottenere 700 ms di tempo di esecuzione. Questo è PGSQL 9.4 e PostGIS 2.2.
Josh,

2

Come nota a margine, esiste una ragionevole possibilità che questo comportamento venga modificato in PostGIS 2.3.0 se si desidera definirlo un bug.

Dai documenti su PostgreSQL

Un numero positivo che indica il costo di esecuzione stimato per la funzione, in unità di cpu_operator_cost. Se la funzione restituisce un set, questo è il costo per riga restituita. Se il costo non viene specificato, si presuppone 1 unità per il linguaggio C e le funzioni interne e 100 unità per le funzioni in tutte le altre lingue. Valori più grandi fanno sì che il pianificatore tenti di evitare di valutare la funzione più spesso del necessario.

Quindi il costo predefinito era 1 (molto economico). D_Withinl'utilizzo di un indice GIST è molto economico. Ma questo è stato aumentato a 100 (per procura dell'interno _ST_DWithin).

Non sono un grande fan del metodo CTE. I CTE sono una barriera di ottimizzazione. Così facendo in questo modo si rimuove un potenziale spazio per l'ottimizzazione futura. Se le impostazioni predefinite di saner lo risolvono, preferirei aggiornare. Alla fine, dobbiamo fare il lavoro e quel metodo funziona chiaramente per te.


1

Grazie al suggerimento di John Powell ho rivisto la query per mettere la condizione di limitazione della contea in una query con / CTE e questo ha migliorato un po 'le prestazioni a 222 ms contro 700. Ancora molto lontano dai .74 ms che ottengo quando i dati sono nella sua proprio tavolo. Non sono ancora sicuro del perché il planner non limiti il ​​set di dati prima di eseguire una costosa funzione postgis, e dovrò provare con set di dati più grandi quando li ho, ma per ora questa sembra essere una soluzione a questa situazione unica.

with points as (
   select p.id, p.geom from point p inner join zipcode z
   on p.zipcode = z.zipcode
   where county = 24
   ) 


SELECT count(points.id)
FROM points
WHERE ST_DWITHIN(points.geom, (ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269)), 16090.0, false)

1
Dovremmo vedere tutti e tre i piani di query e lo schema per la tabella (richiesto nella mia risposta \ d punto).
Evan Carroll,

0

Dovresti creare un indice su zipcode(county, zipcode), che dovrebbe darti un indice scansiona solo su z.

Si consiglia inoltre di sperimentare con btree_gistestensione creazione sia point(zipcode, geom)indice o point(geom, zipcode)e zipcode(zipcode, county)indice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.