Postgis에서 ArcGIS와 같은 속도 획득


62

나는 지금 일년 중 3/4에 Postgis 2.0을 사용해 왔으며 실제로 그것을 사용하는 것을 즐기면서 과도한 쿼리 처리 시간으로 인해 기본적으로 사용 사례에 사용할 수 없게되었습니다.

나는 종종 수십만 개의 다중 다각형을 가진 도시 데이터 세트에 대해 무거운 지오 프로세싱을하는 경향이 있습니다. 이러한 다중 다각형은 때때로 매우 불규칙적으로 형성되며 다중 다각형 당 4 점에서 78,000 점까지 다양합니다.

예를 들어, 525 개의 다중 다각형을 포함하는 관할 구역 데이터 집합과 329,152 개의 다중 다각형으로 구획 데이터 집합을 교차 시키면 총 소비 시간에 대한 다음 통계가 표시됩니다.

ArcGIS 10.0 (on same host with windows 7 OS): 3 minutes
Postgis:56 minutes (not including geometry pre-processing queries)

다시 말해, PostGIs에서 ArcGIS보다이 교차로를 수행하는 데 1500 % 더 많은 시간이 필요합니다. 이것은 더 간단한 쿼리 중 하나입니다!

ArcGIS가 더 빠르게 실행되는 이유 중 하나는 더 나은 색인 때문입니다. 일부 프로그래머는 최근에 이러한 인덱스의 작동 방식을 알아 냈으며 Postgis에서 인덱스를 작성하는 방법 (또는 인덱스를 모방 할 테이블을 작성하는 방법)을 아는 사람이 있는지 궁금합니다. 아마도 이것은 Postgis의 대부분의 속도 문제를 해결할 것입니다. 특히 ArcGIS는 4GB의 RAM 만 사용할 수 있지만 postgis 서버의 경우 최대 4 배를 사용할 수 있기 때문에 어떤 방법이 필요하기를 바랍니다.

물론 postgis가 느리게 실행될 수있는 여러 가지 이유가 있으므로 자세한 시스템 사양을 제공 할 것입니다.

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

또한 VM 자체 작성을 포함하여 postgis를 설정하는 데 사용한 전체 설치 프로세스에 대해서도 자세히 설명합니다 .

또한 conf 파일에서 공유 메모리를 기본 24MB에서 6GB로 늘리고 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

내가 알 수있는 한 이것은 성능면에서 눈에 띄지 않습니다.

이 테스트에 사용한 데이터에 대한 링크는 다음과 같습니다.

  1. 소포 : tcad_parcels_06142012.shp.zip 에서 텍사스 주 오스틴시
  2. 관할 구역 : 관할 경계 에서 오스틴, 텍사스의 도시

데이터를 처리하기 위해 취한 단계는 다음과 같습니다.

ArcGIS

  1. ArcMap에 데이터 세트 추가
  2. 좌표계를 중앙 텍사스 피트로 설정 (2277 번 분할)
  3. 드롭 다운 메뉴에서 교차 도구 사용

포스트 지스

다음을 사용하여 소포 가져 오기 :

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

다음을 사용하여 관할 구역 가져 오기 :

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

구획에서 유효하지 않은 지오메트리를 청소하십시오.

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;

관할 구역에서 유효하지 않은 지오메트리 정리 :

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;

클러스터를 실행하십시오.

cluster;

진공 분석을 실행하십시오.

vacuum analyze;

정리 된 테이블에서 교차를 수행하십시오.

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);

교차로 분석 분석 설명 :

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)

내가 읽은 모든 것에서 교차 쿼리는 효율적이며 쿼리가 깨끗한 지오메트리에서 56 분이 걸리는 것에 대해 내가 뭘 잘못하고 있는지 전혀 모른다!


2
PostGIS에서 속도를 높이기 위해 경계 상자 교차 검사를 추가하는 것은 일반적인 관용구입니다. WHERE 절에 'AND a.geom && b.geom'을 추가하고 그 차이가 얼마나되는지보십시오.
Sean

2
st_intersects ()에는 postgis 2.x에서 교차 테스트를 수행하기 전에 바운딩 박스 쿼리가 포함되어 있으므로 불행히도 시간을 절약 할 수 없습니다.
THX1138

1
EXPLAIN ANALYZE를 사용하여 쿼리를 실행하고 결과를 게시 할 수 있습니까
Nathan W

1
또한 postgis와 arcgis에서 서로 다른 데이터 세트를 실행하고 있음을 알고 있어야합니다.
Nicklas Avén

2
데이터 세트를 살펴볼 수 있습니까?
Nicklas Avén

답변:


87

다른 접근법. 고통이 ST_Intersection에 있고 참 / 거짓 테스트가 빠르다는 것을 알면 교차점을 통과하는 형상의 양을 최소화하려고하면 속도가 빨라질 수 있습니다. 예를 들어, 관할 구역에 완전히 포함 된 소포는 잘릴 필요는 없지만 ST_Intersection은 새로운 지오메트리를 생성 할 필요가 없다는 것을 깨닫기 전에 교차로 오버레이의 일부를 작성하는 데 어려움을 겪을 수 있습니다. 그래서 이건

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,

  st_multi(st_intersection(a.geom,b.geom)) AS geom
FROM
  valid_parcels a, valid_jurisdictions b
WHERE
  st_intersects(a.geom, b.geom) and not st_within(a.geom, b.geom)
UNION ALL
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,
  a.geom AS geom
FROM
  valid_parcels a, valid_jurisdictions b
WHERE
  st_within(a.geom, b.geom);

또는 심지어 터져

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
  a.orig_gid AS parcel_gid,
  b.orig_gid AS jurisdiction_gid,
  CASE 
     WHEN ST_Within(a.geom,b.geom) 
     THEN a.geom
     ELSE ST_Multi(ST_Intersection(a.geom,b.geom)) 
  END AS geom
FROM valid_parcels a
JOIN valid_jurisdictions b
ON ST_Intersects(a.geom, b.geom)

UNION없이 더 빠를 수도 있습니다.


13
감사합니다. 3.63 분! 나는 노동 조합이 더 빠를 것이라고 생각하지 않았을 것입니다. 이 대답은 실제로 지금부터 쿼리를 수행하는 방식을 다시 생각하게 할 것입니다.
THX1138

2
이것은 매우 시원합니다. 내 st_intersection 쿼리가 30 분 이상 걸리는 사례가 있었으며 이제는 어떻게 피할 수 있는지 알고 있습니다. :)
Nathan W

1
이 질문으로 Postgis를 배울 수있었습니다! 나는 Postgis가 Arcgis와 어깨를 나란히하는 것을보고 오늘 잘 자 것이다 :-)
vinayan

2
Martin Davis의 한 가지 더 향상된 기능으로, "in or out?"을 인라인 할 수 있습니다. CASE 문을 사용하여 SELECT에 질문하고 UNION을 피하십시오.
폴 램지

2
UNION두 쿼리에서 중복 된 행을 제거하지만이 두 쿼리는 결과 집합에서 동일한 행을 가질 수 없습니다. 따라서 UNION ALL중복 검사를 건너 뛰는 여기가 적합합니다. ( UNION그 정도를 사용하지는 않지만 일반적으로 사용 하는 시간보다 훨씬 더 자주 사용 UNION ALL
한다는 것을 알았습니다

4

"st_multi(st_intersection(a.geom,b.geom))"부품 을 생략하면 어떻게됩니까?

아래 쿼리가 없으면 동일한 쿼리를 의미하지 않습니까? 나는 당신이 제공 한 데이터에서 그것을 실행했습니다.

INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
  SELECT
    a.orig_gid parcel_gid,
    b.orig_gid jurisdiction_gid,
    a.geom
  FROM
    valid_parcels a, valid_jurisdictions b
  WHERE
    st_intersects(a.geom,b.geom);

구성

Processor: AMD Athlon II X4 635 2.9 GHz 
Memory: 4 GB
Platform: Windows 7 Professional
Potgres Version: 8.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"

결과 분석

"Nested Loop  (cost=0.00..7505.18 rows=217489 width=1580) (actual time=1.994..248405.616 rows=329150 loops=1)"
"  Join Filter: _st_intersects(a.geom, b.geom)"
"  ->  Seq Scan on valid_jurisdictions b  (cost=0.00..37.25 rows=525 width=22621) (actual time=0.054..1.732 rows=525 loops=1)"
"  ->  Index Scan using valid_parcels_index on valid_parcels a  (cost=0.00..11.63 rows=2 width=1576) (actual time=0.068..6.423 rows=1366 loops=525)"
"        Index Cond: (a.geom && b.geom)"
"Total runtime: 280087.497 ms"

아니요, 그는 결과 교차 다각형을 원하지만 쿼리는 바이너리의 참 / 거짓 테스트 부분이 아니라 교차로 생성에 모든 고통이 있음을 잘 보여줍니다. 그리고 교차 코드 생성이 아닌 실제 / 거짓 코드는 고도로 최적화되어 있기 때문에 상당히 기대됩니다.
Paul Ramsey
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.