Ho uno stack TileMill / PostGIS in esecuzione su una VM Ubuntu 12.04 a 8 core su un cloud OpenStack. È la ricostruzione di un sistema molto simile che funzionava bene su hardware molto simile (stesso cloud, ma credo che hardware fisico diverso) la scorsa settimana. Ho provato a ricostruire lo stack esattamente come era (usando alcuni script che avevo creato).
Tutto funziona, ma il database sta eseguendo query in modo lancinante lentamente, che alla fine si manifesta con una generazione di piastrelle molto lenta. Una query di esempio (contare il numero di pub nel raggio di ogni città in Australia), che in precedenza richiedeva qualcosa come 10-20 secondi, ora impiega più di 10 minuti:
explain (analyze, buffers) update places set pubs =
(select count(*) from planet_osm_point p where p.amenity = 'pub' and st_dwithin(p.way,places.way,scope)) +
(select count(*) from planet_osm_polygon p where p.amenity = 'pub' and st_dwithin(p.way,places.way,scope)) ;
Update on places (cost=0.00..948254806.93 rows=9037 width=160) (actual time=623321.558..623321.558 rows=0 loops=1)
Buffers: shared hit=132126300
-> Seq Scan on places (cost=0.00..948254806.93 rows=9037 width=160) (actual time=68.130..622931.130 rows=9037 loops=1)
Buffers: shared hit=132107781
SubPlan 1
-> Aggregate (cost=12.95..12.96 rows=1 width=0) (actual time=0.187..0.188 rows=1 loops=9037)
Buffers: shared hit=158171
-> Index Scan using planet_osm_point_index on planet_osm_point p (cost=0.00..12.94 rows=1 width=0) (actual time=0.163..0.179 rows=0 loops=9037)
Index Cond: (way && st_expand(places.way, (places.scope)::double precision))
Filter: ((amenity = 'pub'::text) AND (places.way && st_expand(way, (places.scope)::double precision)) AND _st_dwithin(way, places.way, (places.scope)::double precision))
Buffers: shared hit=158171
SubPlan 2
-> Aggregate (cost=104917.24..104917.25 rows=1 width=0) (actual time=68.727..68.728 rows=1 loops=9037)
Buffers: shared hit=131949237
-> Seq Scan on planet_osm_polygon p (cost=0.00..104917.24 rows=1 width=0) (actual time=68.138..68.716 rows=0 loops=9037)
Filter: ((amenity = 'pub'::text) AND (way && st_expand(places.way, (places.scope)::double precision)) AND (places.way && st_expand(way, (places.scope)::double precision)) AND _st_dwithin(way, places.way, (places.scope)::double precision))
Buffers: shared hit=131949237
Total runtime: 623321.801 ms
(Includo questa query come sintomo, non direttamente il problema da risolvere. Questa query specifica viene eseguita solo una volta alla settimana).
Il server ha 32 GB di RAM e ho configurato Postgres come segue (seguendo i consigli trovati sul web):
shared_buffers = 8GB
autovacuum = on
effective_cache_size = 8GB
work_mem = 128MB
maintenance_work_mem = 64MB
wal_buffers = 1MB
checkpoint_segments = 10
iostat
non mostra nulla da leggere, un po 'di dati vengono scritti (non ho idea di dove o perché) e CPU inattiva al 95%:
avg-cpu: %user %nice %system %iowait %steal %idle
5.40 0.00 0.00 0.11 0.00 94.49
Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn
vda 0.20 0.00 0.80 0 8
vdb 2.30 0.00 17.58 0 176
Esempio di output da vmstat
:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
...
1 0 0 18329748 126108 12600436 0 0 0 18 148 140 5 0 95 0
2 0 0 18329400 126124 12600436 0 0 0 9 173 228 5 0 95 0
Stringendo a botte, ho spostato la directory dei dati di Postgres da vda a vdb, ma ovviamente non ha fatto differenza.
Quindi sono in perdita. Perché Postgres utilizza solo il 5% della CPU disponibile quando non è in attesa di alcun I / O? Gradirei qualsiasi suggerimento per ulteriori indagini, altri strumenti, cose casuali da provare.
Aggiornare
Ho eseguito l'istantanea del server e l'ho avviato su una parte diversa dello stesso cloud (una zona di disponibilità diversa). I risultati sono stati un po 'strani. vmstat
su questo server riporta un utilizzo della CPU del 12% (che ora intendo come il valore atteso per una singola query Postgres su una VM a 8 core), sebbene il tempo di esecuzione della query effettivo sia praticamente identico (630 secondi contro 623).
Ora mi rendo conto che questa particolare query non è probabilmente un buon esempio per questo motivo: può usare solo un core ed è un update
(mentre il rendering delle piastrelle è solo select
s).
Inoltre non ho notato explain
che apparentemente planet_osm_polygon
non sta usando un indice. Potrebbe essere la causa, quindi lo inseguirò dopo.
Update2
Il problema sembra sicuramente essere che gli indici planet_osm_polygon siano / non vengano utilizzati. Ce ne sono due (uno creato da osm2pgsql, uno creato da me seguendo una guida casuale):
CREATE INDEX idx_planet_osm_polygon_tags
ON planet_osm_polygon
USING gist
(tags);
CREATE INDEX planet_osm_polygon_pkey
ON planet_osm_polygon
USING btree
(osm_id);
Le statistiche su planet_osm_polygon e planet_osm_point sono piuttosto rivelatrici, penso:
planet_osm_polygon:
Sequential Scans 194204
Sequential Tuples Read 60981018608
Index Scans 1574
Index Tuples Fetched 0
planet_osm_point:
Sequential Scans 1142
Sequential Tuples Read 12960604
Index Scans 183454
Index Tuples Fetched 43427685
Se ho letto bene, Postgres ha cercato il pianeta_osm_polygon 1574 volte, ma in realtà non ha mai trovato nulla, quindi ha fatto un numero ridicolmente elevato di ricerche di forza bruta.
La nuova domanda: perché?
Mistero risolto
Grazie alla risposta di Frederik Ramm , la risposta risulta abbastanza semplice: per qualche motivo non esisteva un indice spaziale. È stato banale rigenerarli:
create index planet_osm_polygon_polygon on planet_osm_polygon using gist(way);
create index planet_osm_polygon_point on planet_osm_point using gist(way);
L'esecuzione di quella query ora richiede 4,6 secondi. Gli indici spaziali contano! :)