Połączenie klucza / wartości Hstore i zapytania przestrzennego jest zbyt wolne, aby obsługiwać większe wyciągi OSM

13

Próbuję obliczyć statystyki dla danych OSM za pomocą PostgreSQL 9.3.5 i PostGIS 2.1.4. Zacząłem od małego wyciągu z bawarii, który pobrałem z Geofabrik. Schemat db jest normalnym schematem API 0.6, dane zostały zaimportowane za pomocą zrzutu do Postgres (za pomocą skryptów pgsnapshot_schema_0.6 * .sql, które są dostarczane z osmozą). Wykonano również PODCIŚNIENIE ANALIZOWE.

Jedyną niestandardową rzeczą, której używam, jest tabela wielokątów, która zawiera wieloboki dla wszystkich relacji granic administracyjnych. Geometria wielokąta nie została w żaden sposób uproszczona.

To, co teraz próbuję osiągnąć, to zliczenie wszystkich węzłów, które znajdują się w admin = 6 granicach bawarii. Oto moje zapytanie SQL:

SELECT relpoly.id, count(node) 
FROM bavaria.relpolygons relpoly, bavaria.nodes node
WHERE relpoly.tags @> '"boundary"=>"administrative","admin_level"=>"6"'::hstore 
AND ST_Intersects(relpoly.geom, node.geom)
GROUP BY relpoly.id;

Środowisko wykonawcze tego zapytania jest straszne, ponieważ Postgres wykonuje zagnieżdżone sprzężenie pętli i skanuje wszystkie węzły pod kątem każdej granicy admin = 6. Do Twojej wiadomości, Bawaria jest podzielona na 98 admin = 6 wielokątów, aw ekstrakcie z bawarii znajduje się około 30 milionów węzłów.

Czy można uniknąć tego nieoptymalnego wykonania zapytania i powiedzieć Postgresowi, że powinien skanować wszystkie węzły tylko raz (np. Zwiększając licznik dla odpowiedniego wielokąta w zestawie wyników lub używając wskazówek)?

Edytować:

1) w węzłach bawarii istnieje indeks przestrzenny:

CREATE INDEX idx_nodes_geom ON bavaria.nodes USING gist (geom);

2) plan zapytań wygląda następująco:

HashAggregate  (cost=284908.49..284908.75 rows=26 width=103)
  ->  Nested Loop  (cost=111.27..283900.80 rows=201537 width=103)
        ->  Bitmap Heap Scan on relpolygons relpoly  (cost=4.48..102.29 rows=26 width=5886)
              Recheck Cond: (tags @> '"boundary"=>"administrative", "admin_level"=>"6"'::hstore)
              ->  Bitmap Index Scan on relpolygons_geom_tags  (cost=0.00..4.47 rows=26 width=0)
                    Index Cond: (tags @> '"boundary"=>"administrative", "admin_level"=>"6"'::hstore)
        ->  Bitmap Heap Scan on nodes node  (cost=106.79..10905.50 rows=983 width=127)
              Recheck Cond: (relpoly.geom && geom)
              Filter: _st_intersects(relpoly.geom, geom)
              ->  Bitmap Index Scan on idx_nodes_geom  (cost=0.00..106.55 rows=2950 width=0)
                    Index Cond: (relpoly.geom && geom)

3)

Utworzyłem następujące dwa indeksy, ale plan zapytań (i środowisko wykonawcze) nie zmieniły się

CREATE INDEX relpolygons_tags_boundary on bavaria.relpolygons( (tags->'boundary') );
CREATE INDEX relpolygons_tags_admin on bavaria.relpolygons( (tags->'admin_level') );
ANALYZE bavaria.relpolygons;
Alf Kortig
źródło
1
Czy masz indeks przestrzenny w bavaria.nodes?
user30184,
tak, zredagowałem pytanie i
podałem
3
Dwie opcje. 1 - dodaj indeks dla tagów hstore. 2 - wyodrębnij tagi używane do zapytania ( boundaryi admin_level) do dodatkowych kolumn w tabeli i użyj ich bezpośrednio.
BradHards,
Zobacz edit (3): dodano dwa indeksy, ale nie wprowadzono żadnych zmian w planie zapytań ani w środowisku wykonawczym.
Alf Kortig
Po kilku testach nie jestem już pewien, czy utworzyłem prawidłowe indeksy w (3). Do tej pory udało mi się stworzyć indeks dla -> i? operatorzy hstore. Jednak używam @> w moim zapytaniu
Alf Kortig

Odpowiedzi:

5

Najlepszym sposobem na indeksowanie tagów hstore jest użycie indeksów GIN lub GIST, które z dokumentów obsługują @>,?,? I? | operatorzy , to znaczy wyszukują klucze i pary klucz / wartość. Podejście do używania funkcji do wyodrębniania znaczników dla indeksu B-drzewa jest uzasadnione, ale ponieważ sprawdzane są również określone pary klucz / wartość, analizator wybrał pełny skan tabeli.

Nie mam dostępu do bavaria.relpolygons, ale w oparciu o podobne zapytanie dla OSM UK dotyczące ograniczeń prędkości i znaczników autostrad, otrzymuję to dla mojego wyjaśnienia na temat następującego zapytania:

SELECT count(*) 
 FROM ways 
WHERE tags @> 'highway=>motorway'::hstore 
 AND tags @> 'maxspeed=>"50 mph"'::hstore;


Aggregate  (cost=48.66..48.67 rows=1 width=0)
    ->  Index Scan using ix_ways_tags_gist on ways  (cost=0.42..48.64 rows=11 width=0)
     Index Cond: ((tags @> '"highway"=>"motorway"'::hstore) AND (tags @> '"maxspeed"=>"50 mph"'::hstore))

który pokazuje bezpośredni skan indeksu (przy użyciu indeksu gist), co jest zachęcające dla tabeli z 10 milionami wierszy. Indeks został utworzony za pomocą prostego:

CREATE INDEX ix_ways_tags_gist ON ways USING gist (tags);

Chociaż nie mogę sprawdzić stanu przestrzennego, wydaje mi się, że jest mniej selektywny niż

GDZIE relpoly.tags @> '”granica” => „administracyjny”, „poziom_administracyjny” => „6”' :: hstore.

i dlatego byłby używany tylko w przypadku warunku ponownego sprawdzenia.

Jest też świetna odpowiedź SO na różnicę między indeksami GIN i GIST . Ogólne odkrycie jest takie, że chociaż indeksy GIN są większe i wolniejsze w budowie, są znacznie szybsze w przypadku problemów z wyszukiwaniem tekstu.

Przepraszam, że odpowiadam tak późno, ale ostatnio robię podobne prace na OSM i hstore, i odkryłem, że nie tylko raz zaznaczyłem to pytanie, ale że teraz mogę na nie odpowiedzieć: D.

John Powell
źródło