À quelle vitesse devrais-je m'attendre à ce que PostGIS géocode des adresses bien formatées?
J'ai installé PostgreSQL 9.3.7 et PostGIS 2.1.7, chargé les données nationales et toutes les données des États, mais j'ai trouvé que le géocodage était beaucoup plus lent que prévu. Ai-je placé mes attentes trop haut? J'obtiens en moyenne 3 géocodages individuels par seconde. Je dois en faire environ 5 millions et je ne veux pas attendre trois semaines pour cela.
Il s'agit d'une machine virtuelle pour le traitement des matrices R géantes et j'ai installé cette base de données sur le côté afin que la configuration puisse paraître un peu maladroite. Si une modification majeure de la configuration de la VM peut aider, je peux modifier la configuration.
Spécifications matérielles
Mémoire: processeurs de 65 Go: 6
lscpu
me donne ceci:
# 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 is centos, uname -rv
donne ceci:
# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015
Configuration postgresql
> 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"
Sur la base des suggestions précédentes concernant ces types de requêtes, j'ai augmenté shared_buffers
le postgresql.conf
fichier à environ 1/4 de la RAM disponible et la taille du cache efficace à la moitié de la RAM:
shared_buffers = 16096MB
effective_cache_size = 31765MB
J'ai installed_missing_indexes()
et (après avoir résolu les insertions en double dans certaines tables) je n'ai eu aucune erreur.
Exemple de géocodage SQL # 1 (batch) ~ le temps moyen est de 2,8 / sec
Je suis l'exemple de http://postgis.net/docs/Geocode.html , qui m'a fait créer une table contenant l'adresse à géocoder, puis faire un SQL 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";
J'utilise une taille de lot de 1000 au-dessus et elle revient en 337,70 secondes. C'est un peu plus lent pour les petits lots.
Exemple de géocodage SQL # 2 (ligne par ligne) ~ le temps moyen est de 1,2 / sec
Lorsque je fouille dans mes adresses en faisant les géocodes un à la fois avec une déclaration qui ressemble à ceci (btw, l'exemple ci-dessous a pris 4,14 secondes),
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;
c'est un peu plus lent (2,5 fois par enregistrement) mais je peux regarder la distribution des temps de requête et voir que c'est une minorité de longues requêtes qui ralentissent le plus (seuls les premiers 2600 sur 5 millions ont des temps de recherche). Autrement dit, les 10% supérieurs prennent en moyenne environ 100 ms, les 10% inférieurs en moyenne 3,69 secondes, tandis que la moyenne est de 754 ms et la médiane est de 340 ms.
# 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]
D'autres pensées
Si je ne peux pas obtenir un ordre de grandeur d'augmentation des performances, je pensais que je pourrais au moins faire une supposition éclairée sur la prévision des temps de géocodage lents, mais il n'est pas évident pour moi pourquoi les adresses plus lentes semblent prendre beaucoup plus de temps. J'exécute l'adresse d'origine via une étape de normalisation personnalisée pour m'assurer qu'elle est bien formatée avant que la geocode()
fonction ne l'obtienne:
sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")
où myAddress
est une [Address], [City], [ST] [Zip]
chaîne compilée à partir d'une table d'adresses utilisateur à partir d'une base de données non postgresql.
J'ai essayé (échoué) d'installer l' pagc_normalize_address
extension mais il n'est pas clair que cela apportera le type d'amélioration que je recherche.
Modifié pour ajouter des informations de surveillance selon la suggestion
Performance
Un processeur est indexé: [modifier, un seul processeur par requête, j'ai donc 5 processeurs inutilisés]
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
Exemple d'activité de disque sur la partition de données pendant qu'un proc est indexé à 100%: [modifier: un seul processeur utilisé par cette requête]
# 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
Analysez ce SQL
C'est à partir EXPLAIN ANALYZE
de cette requête:
"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"
Voir une meilleure répartition sur http://explain.depesz.com/s/vogS