¿Qué tan rápido debo esperar que PostGIS geocodifique direcciones bien formateadas?
Instalé PostgreSQL 9.3.7 y PostGIS 2.1.7, cargué los datos de la nación y todos los datos de los estados, pero he encontrado que la geocodificación es mucho más lenta de lo que esperaba. ¿Puse mis expectativas demasiado altas? Estoy obteniendo un promedio de 3 códigos geográficos individuales por segundo. Necesito hacer unos 5 millones y no quiero esperar tres semanas para esto.
Esta es una máquina virtual para procesar matrices R gigantes e instalé esta base de datos en el lateral para que la configuración pueda parecer un poco tonta. Si una alteración importante en la configuración de la VM ayudará, puedo alterar la configuración.
Especificaciones de hardware
Memoria: procesadores de 65 GB: 6
lscpu
me da esto:
# 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 es centos, uname -rv
da esto:
# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015
Postgresql config
> 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"
Según las sugerencias anteriores para este tipo de consultas, aumenté shared_buffers
el postgresql.conf
archivo a aproximadamente 1/4 de RAM disponible y el tamaño efectivo de caché a 1/2 de RAM:
shared_buffers = 16096MB
effective_cache_size = 31765MB
Tengo installed_missing_indexes()
y (después de resolver inserciones duplicadas en algunas tablas) no tuve ningún error.
Ejemplo de codificación geográfica SQL # 1 (lote) ~ el tiempo medio es 2.8 / seg
Estoy siguiendo el ejemplo de http://postgis.net/docs/Geocode.html , que me hace crear una tabla que contiene la dirección para geocodificar, y luego hacer 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";
Estoy usando un tamaño de lote de 1000 arriba y regresa en 337.70 segundos. Es un poco más lento para lotes más pequeños.
Ejemplo de SQL de codificación geográfica # 2 (fila por fila) ~ el tiempo medio es 1.2 / seg
Cuando busco en mis direcciones haciendo los códigos geográficos uno a la vez con una declaración que se ve así (por cierto, el ejemplo a continuación tomó 4.14 segundos),
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 un poco más lento (2.5x por registro), pero puedo ver la distribución de los tiempos de consulta y ver que es una minoría de consultas largas las que lo ralentizan más (solo los primeros 2600 de 5 millones tienen tiempos de búsqueda). Es decir, el 10% superior está tomando un promedio de aproximadamente 100 ms, el 10% inferior promedio 3.69 segundos, mientras que la media es 754 ms y la mediana es 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]
Otros pensamientos
Si no puedo obtener un aumento de orden de magnitud en el rendimiento, pensé que al menos podría hacer una suposición educada sobre la predicción de tiempos lentos de geocodificación, pero no es obvio para mí por qué las direcciones más lentas parecen estar tardando mucho más. Estoy ejecutando la dirección original a través de un paso de normalización personalizado para asegurarme de que esté bien formateado antes de que la geocode()
función lo obtenga:
sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")
donde myAddress
es una [Address], [City], [ST] [Zip]
cadena compilada de una tabla de direcciones de usuario de una base de datos no postgresql.
Intenté (fallé) instalar la pagc_normalize_address
extensión, pero no está claro que esto traerá el tipo de mejora que estoy buscando.
Editado para agregar información de monitoreo según la sugerencia
Actuación
Una CPU está vinculada: [editar, solo un procesador por consulta, por lo que tengo 5 CPU no utilizadas]
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
Muestra de actividad de disco en la partición de datos mientras un proceso está vinculado al 100%: [editar: solo un procesador en uso por esta consulta]
# 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
Analiza ese SQL
Esto es de EXPLAIN ANALYZE
esa consulta:
"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"
Vea un mejor desglose en http://explain.depesz.com/s/vogS