Wie schnell sollte ich damit rechnen, dass PostGIS gut formatierte Adressen geocodiert?
Ich habe PostgreSQL 9.3.7 und PostGIS 2.1.7 installiert, die Länderdaten und alle Zustandsdaten geladen, aber die Geokodierung ist viel langsamer als erwartet. Habe ich meine Erwartungen zu hoch gesetzt? Ich erhalte durchschnittlich 3 einzelne Geocodes pro Sekunde. Ich muss ungefähr 5 Millionen machen und ich möchte nicht drei Wochen darauf warten.
Dies ist eine virtuelle Maschine für die Verarbeitung von Riesen-R-Matrizen, und ich habe diese Datenbank auf der Seite installiert, damit die Konfiguration möglicherweise etwas doof aussieht. Wenn eine größere Änderung in der Konfiguration der VM hilfreich ist, kann ich die Konfiguration ändern.
Hardware-Spezifikationen
Speicher: 65 GB Prozessoren: 6 lscpu
gibt mir Folgendes
:
# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 6
On-line CPU(s) list: 0-5
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 6
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 58
Stepping: 0
CPU MHz: 2400.000
BogoMIPS: 4800.00
Hypervisor vendor: VMware
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 30720K
NUMA node0 CPU(s): 0-5
OS ist Centos, uname -rv
gibt dies:
# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015
Postgresql-Konfig
> select version()
"PostgreSQL 9.3.7 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit"
> select PostGIS_Full_version()
POSTGIS="2.1.7 r13414" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.2, released 2012/10/08" LIBXML="2.7.6" LIBJSON="UNKNOWN" TOPOLOGY RASTER"
Basierend auf früheren Vorschlägen für diese Art von Abfragen habe ich shared_buffers
in der postgresql.conf
Datei ungefähr 1/4 des verfügbaren Arbeitsspeichers und die effektive Cache-Größe auf 1/2 des Arbeitsspeichers erhöht :
shared_buffers = 16096MB
effective_cache_size = 31765MB
Ich habe installed_missing_indexes()
und (nach dem Auflösen doppelter Einfügungen in einige Tabellen) keine Fehler gehabt.
Geocodierungs-SQL-Beispiel Nr. 1 (Batch) ~ Die durchschnittliche Zeit beträgt 2,8 / s
Ich folge dem Beispiel von http://postgis.net/docs/Geocode.html , bei dem ich eine Tabelle mit der Adresse zum Geocodieren erstelle und dann eine SQL durchführe UPDATE
:
UPDATE addresses_to_geocode
SET (rating, longitude, latitude,geo)
= ( COALESCE((g.geom).rating,-1),
ST_X((g.geom).geomout)::numeric(8,5),
ST_Y((g.geom).geomout)::numeric(8,5),
geo )
FROM (SELECT "PatientId" as PatientId
FROM addresses_to_geocode
WHERE "rating" IS NULL ORDER BY PatientId LIMIT 1000) As a
LEFT JOIN (SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom
FROM addresses_to_geocode As ag
WHERE ag.rating IS NULL ORDER BY PatientId LIMIT 1000) As g ON a.PatientId = g.PatientId
WHERE a.PatientId = addresses_to_geocode."PatientId";
Ich verwende eine Stapelgröße von 1000 über und es kehrt in 337.70 Sekunden zurück. Bei kleineren Chargen ist es etwas langsamer.
Geokodierungs-SQL-Beispiel Nr. 2 (Zeile für Zeile) ~ Die durchschnittliche Zeit beträgt 1,2 / Sek
Wenn ich in meine Adressen grabe, indem ich die Geokodierungen nacheinander mit einer Anweisung durchführe, die so aussieht (übrigens hat das folgende Beispiel 4,14 Sekunden gedauert),
SELECT g.rating, ST_X(g.geomout) As lon, ST_Y(g.geomout) As lat,
(addy).address As stno, (addy).streetname As street,
(addy).streettypeabbrev As styp, (addy).location As city,
(addy).stateabbrev As st,(addy).zip
FROM geocode('6433 DROMOLAND Cir NW, MASSILLON, OH 44646',1) As g;
Es ist etwas langsamer (2,5x pro Datensatz), aber ich kann die Verteilung der Abfragezeiten betrachten und feststellen, dass es eine Minderheit von langwierigen Abfragen ist, die dies am meisten verlangsamen (nur die ersten 2600 von 5 Millionen haben Nachschlagezeiten). Das heißt, die oberen 10% benötigen durchschnittlich etwa 100 ms, die unteren 10% durchschnittlich 3,69 Sekunden, während der Mittelwert 754 ms und der Median 340 ms beträgt.
# Just some interaction with the data in R
> range(lookupTimes[1:2600])
[1] 0.00 11.54
> median(lookupTimes[1:2600])
[1] 0.34
> mean(lookupTimes[1:2600])
[1] 0.7541808
> mean(sort(lookupTimes[1:2600])[1:260])
[1] 0.09984615
> mean(sort(lookupTimes[1:2600],decreasing=TRUE)[1:260])
[1] 3.691269
> hist(lookupTimes[1:2600]
Andere Gedanken
Wenn ich die Leistung nicht um eine Größenordnung steigern kann, habe ich gedacht, ich könnte zumindest eine fundierte Vermutung über die Vorhersage langsamer Geocode-Zeiten anstellen, aber es ist mir nicht klar, warum die langsameren Adressen so viel länger zu dauern scheinen. Ich führe die ursprüngliche Adresse durch einen benutzerdefinierten Normalisierungsschritt, um sicherzustellen, dass sie ordnungsgemäß formatiert ist, bevor sie von der geocode()
Funktion abgerufen wird:
sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")
Dabei myAddress
handelt es sich um eine [Address], [City], [ST] [Zip]
Zeichenfolge, die aus einer Benutzeradressentabelle aus einer Nicht-Postgresql-Datenbank kompiliert wurde.
Ich habe versucht (gescheitert), die pagc_normalize_address
Erweiterung zu installieren, aber es ist nicht klar, dass dies die Art von Verbesserung bringt, die ich suche.
Bearbeitet, um Überwachungsinformationen gemäß Vorschlag hinzuzufügen
Performance
Eine CPU ist gekoppelt: [Bearbeiten, nur ein Prozessor pro Abfrage, daher habe ich 5 nicht verwendete CPUs]
top - 14:10:26 up 1 day, 3:11, 4 users, load average: 1.02, 1.01, 0.93
Tasks: 219 total, 2 running, 217 sleeping, 0 stopped, 0 zombie
Cpu(s): 15.4%us, 1.5%sy, 0.0%ni, 83.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 65056588k total, 64613476k used, 443112k free, 97096k buffers
Swap: 262139900k total, 77164k used, 262062736k free, 62745284k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3130 postgres 20 0 16.3g 8.8g 8.7g R 99.7 14.2 170:14.06 postmaster
11139 aolsson 20 0 15140 1316 932 R 0.3 0.0 0:07.78 top
11675 aolsson 20 0 135m 1836 1504 S 0.3 0.0 0:00.01 wget
1 root 20 0 19364 1064 884 S 0.0 0.0 0:01.84 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.06 kthreadd
Beispiel für die Festplattenaktivität auf der Datenpartition, während ein Prozessor zu 100% gebunden ist: [Bearbeiten: Nur ein Prozessor wird von dieser Abfrage verwendet.]
# dstat -tdD dm-3 1
----system---- --dsk/dm-3-
date/time | read writ
12-06 14:06:36|1818k 3632k
12-06 14:06:37| 0 0
12-06 14:06:38| 0 0
12-06 14:06:39| 0 0
12-06 14:06:40| 0 40k
12-06 14:06:41| 0 0
12-06 14:06:42| 0 0
12-06 14:06:43| 0 8192B
12-06 14:06:44| 0 8192B
12-06 14:06:45| 120k 60k
12-06 14:06:46| 0 0
12-06 14:06:47| 0 0
12-06 14:06:48| 0 0
12-06 14:06:49| 0 0
12-06 14:06:50| 0 28k
12-06 14:06:51| 0 96k
12-06 14:06:52| 0 0
12-06 14:06:53| 0 0
12-06 14:06:54| 0 0 ^C
Analysieren Sie diese SQL
Dies ist aus EXPLAIN ANALYZE
dieser Abfrage:
"Update on addresses_to_geocode (cost=1.30..8390.04 rows=1000 width=272) (actual time=363608.219..363608.219 rows=0 loops=1)"
" -> Merge Left Join (cost=1.30..8390.04 rows=1000 width=272) (actual time=110.934..324648.385 rows=1000 loops=1)"
" Merge Cond: (a.patientid = g.patientid)"
" -> Nested Loop (cost=0.86..8336.82 rows=1000 width=184) (actual time=10.676..34.241 rows=1000 loops=1)"
" -> Subquery Scan on a (cost=0.43..54.32 rows=1000 width=32) (actual time=10.664..18.779 rows=1000 loops=1)"
" -> Limit (cost=0.43..44.32 rows=1000 width=4) (actual time=10.658..17.478 rows=1000 loops=1)"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode addresses_to_geocode_1 (cost=0.43..195279.22 rows=4449758 width=4) (actual time=10.657..17.021 rows=1000 loops=1)"
" Filter: (rating IS NULL)"
" Rows Removed by Filter: 24110"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode (cost=0.43..8.27 rows=1 width=152) (actual time=0.010..0.013 rows=1 loops=1000)"
" Index Cond: ("PatientId" = a.patientid)"
" -> Materialize (cost=0.43..18.22 rows=1000 width=96) (actual time=100.233..324594.558 rows=943 loops=1)"
" -> Subquery Scan on g (cost=0.43..15.72 rows=1000 width=96) (actual time=100.230..324593.435 rows=943 loops=1)"
" -> Limit (cost=0.43..5.72 rows=1000 width=42) (actual time=100.225..324591.603 rows=943 loops=1)"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode ag (cost=0.43..23534259.93 rows=4449758000 width=42) (actual time=100.225..324591.146 rows=943 loops=1)"
" Filter: (rating IS NULL)"
" Rows Removed by Filter: 24110"
"Total runtime: 363608.316 ms"
Eine bessere Aufschlüsselung finden Sie unter http://explain.depesz.com/s/vogS