He estado usando Postgis 2.0 durante 3/4 de año y aunque realmente disfruto usarlo, el tiempo excesivo de procesamiento de consultas lo ha dejado básicamente inutilizable para mi caso de uso.
Tiendo a hacer geoprocesamiento pesado en conjuntos de datos municipales que a menudo tienen cientos de miles de multipolígonos. Estos multipolígonos a veces tienen una forma muy irregular y pueden variar de 4 puntos a 78,000 puntos por multipolígono.
Por ejemplo, cuando interseco un conjunto de datos de parcela con 329,152 multipolígonos con un conjunto de datos de jurisdicción que contiene 525 multipolígonos, obtengo las siguientes estadísticas para el tiempo total consumido:
ArcGIS 10.0 (on same host with windows 7 OS): 3 minutes
Postgis:56 minutes (not including geometry pre-processing queries)
En otras palabras, se requiere un 1500% más de tiempo para hacer esta intersección en Postgis que en ArcGIS, ¡ y esta es una de mis consultas más simples!
Una de las razones por las que ArcGIS supuestamente se ejecuta más rápido se debe a mejores índices. Algunos programadores descubrieron recientemente cómo funcionan estos índices y me pregunto si alguien sabe cómo construir estos índices en Postgis (o crear tablas que imiten los índices). Quizás esto resolvería la mayoría de los problemas de velocidad en Postgis. Solo puedo esperar que haya alguna forma, especialmente porque ArcGIS solo puede usar 4 GB de RAM, ¡mientras que podría usar hasta 4 veces más para mi servidor postgis!
Por supuesto, hay muchas razones por las que postgis puede ejecutarse lentamente, por lo que proporcionaré una versión detallada de las especificaciones de mi sistema:
Machine: Dell XPS 8300
Processor: i7-2600 CPU @ 3.40 GHz 3.40 GHz
Memory: Total Memory 16.0 GB (10.0 GB on virtual machine)
Platform: Ubuntu Server 12.04 Virtual Box VM
Potgres Version: 9.1.4
Postgis Version: POSTGIS="2.0.1 r9979" GEOS="3.3.5-CAPI-1.7.5" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.1, released 2012/05/15" LIBXML="2.7.8" LIBJSON="UNKNOWN" TOPOLOGY RASTER
También aumenté la memoria compartida de los 24 MB predeterminados a 6 GB en el archivo conf y ejecuté los siguientes comandos para permitir la ejecución de postgres:
sudo sysctl -w kernel.shmmax=7516192768 (I know this setting is deleted every time you restart the OS)
sudo /etc/init.d/postgresql restart
Por lo que puedo decir, esto no hace absolutamente nada notable en términos de rendimiento.
Aquí hay enlaces a los datos que utilicé para esta prueba:
- Paquetes: tcad_parcels_06142012.shp.zip de la ciudad de Austin, TX
- Jurisdicciones: límites jurisdiccionales de la ciudad de Austin, TX
Estos son los pasos que tomé para procesar los datos:
ArcGIS
- Agregar conjuntos de datos a ArcMap
- Establecer el sistema de coordenadas en pies centrales de texas (srid 2277)
- Usar la herramienta de intersección del menú desplegable
Postgis
Importar paquetes usando:
shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "tcad_parcels_06142012.shp" "public"."tcad_parcels_06142012" |psql -d postgis_testing -U postgres -h local_ip -p 5432
Jurisdicciones de importación utilizando:
shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "jurisdictions.shp" "public"."jurisdictions" |psql -d postgis_testing -U postgres -h local_ip -p 5432
Limpiar geometría inválida en parcelas:
DROP TABLE IF EXISTS valid_parcels;
CREATE TABLE valid_parcels(
gid serial PRIMARY KEY,
orig_gid integer,
geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_parcels USING gist (geom);
INSERT INTO valid_parcels(orig_gid,geom)
SELECT
gid
orig_gid,
st_multi(st_makevalid(geom))
FROM
tcad_parcels_06142012;
CLUSTER valid_parcels USING valid_parcels_geom_idx;
Limpiar geometría inválida en jurisdicciones:
DROP TABLE IF EXISTS valid_jurisdictions;
CREATE TABLE valid_jurisdictions(
gid serial PRIMARY KEY,
orig_gid integer,
geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_jurisdictions USING gist (geom);
INSERT INTO valid_jurisdictions(orig_gid,geom)
SELECT
gid
orig_gid,
st_multi(st_makevalid(geom))
FROM
jurisdictions;
CLUSTER valid_jurisdictions USING valid_jurisdictions_geom_idx;
Ejecutar clúster:
cluster;
Ejecute el análisis de vacío:
vacuum analyze;
Realizar intersección en mesas limpias:
CREATE TABLE parcel_jurisdictions(
gid serial primary key,
parcel_gid integer,
jurisdiction_gid integer,
isect_geom geometry(multipolygon,2277)
);
CREATE INDEX ON parcel_jurisdictions using gist (isect_geom);
INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
a.orig_gid parcel_gid,
b.orig_gid jurisdiction_gid,
st_multi(st_intersection(a.geom,b.geom))
FROM
valid_parcels a, valid_jurisdictions b
WHERE
st_intersects(a.geom,b.geom);
Explicar Analizar consulta de intersección:
Total runtime: 3446860.731 ms
Index Cond: (geom && b.geom)
-> Index Scan using valid_parcels_geom_idx on valid_parcels a (cost=0.00..11.66 rows=2 width=1592) (actual time=0.030..4.596 rows=1366 loops=525)
-> Seq Scan on valid_jurisdictions b (cost=0.00..113.25 rows=525 width=22621) (actual time=0.009..0.755 rows=525 loops=1)
Nested Loop (cost=0.00..61428.74 rows=217501 width=24213) (actual time=2.625..3445946.889 rows=329152 loops=1)
Join Filter: _st_intersects(a.geom, b.geom)
De todo lo que he leído, mi consulta de intersección es eficiente y no tengo ni idea de lo que estoy haciendo mal para que la consulta tarde 56 minutos en geometría limpia.