รับความเร็วเหมือน ArcGIS ใน Postgis


62

ฉันใช้ Postgis 2.0 เป็นเวลา 3/4 ของปีแล้วและในขณะที่ฉันสนุกกับการใช้งานจริง ๆ เวลาที่ใช้ในการประมวลผลแบบสอบถามที่มากเกินไปทำให้มันใช้ไม่ได้กับกรณีการใช้งานของฉัน

ฉันมักจะทำการประมวลผลทางภูมิศาสตร์อย่างหนักในชุดข้อมูลเทศบาลซึ่งมักจะมีหลายหมื่นหลายพัน รูปหลายเหลี่ยมเหล่านี้บางครั้งมีรูปร่างที่ผิดปกติมากและอาจแตกต่างกันจาก 4 คะแนนเป็น 78,000 จุดต่อหลายรูปหลายเหลี่ยม

ตัวอย่างเช่นเมื่อฉันตัดชุดข้อมูลพัสดุภัณฑ์ด้วยชุดข้อมูลจำนวน 329,152 ชุดโดยมีชุดเขตอำนาจศาลที่ประกอบด้วยชุดข้อมูลจำนวน 525 ชุดฉันจะได้รับสถิติต่อไปนี้สำหรับเวลาทั้งหมดที่ใช้ไป:

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

กล่าวอีกนัยหนึ่งมันต้องใช้เวลาในการทำจุดตัดนี้ใน Postgis มากกว่า 1500% กว่าใน ArcGIS - และนี่คือหนึ่งในข้อความค้นหาที่เรียบง่ายของฉัน!

เหตุผลหนึ่งที่ ArcGIS คาดว่าจะทำงานได้เร็วกว่านั้นก็เนื่องมาจากดัชนีที่ดีขึ้น โปรแกรมเมอร์บางคนเพิ่งรู้ว่าดัชนีเหล่านี้ทำงานอย่างไรและฉันสงสัยว่าใครรู้วิธีสร้างดัชนีเหล่านี้ใน Postgis (หรือสร้างตารางที่จะเลียนแบบดัชนี) บางทีนี่อาจช่วยแก้ปัญหาความเร็วส่วนใหญ่ใน Postgis ฉันหวังว่าคงมีบางทางเท่านั้นโดยเฉพาะอย่างยิ่งเนื่องจาก ArcGIS สามารถใช้ RAM ได้ 4 GB ในขณะที่ฉันสามารถใช้งานได้มากถึง 4 เท่าสำหรับเซิร์ฟเวอร์ postgis ของฉัน!

แน่นอนมีหลายเหตุผลที่ 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

ฉันยังรายละเอียดขั้นตอนการติดตั้งทั้งหมดที่ผมใช้ในการตั้งค่า PostGIS รวมถึงการสร้างของ VM ตัวเอง

ฉันยังเพิ่มหน่วยความจำที่แชร์จาก 24MB เริ่มต้นเป็น 6 GB ในไฟล์ conf และรันคำสั่งต่อไปนี้เพื่ออนุญาตให้ 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. Parcels: tcad_parcels_06142012.shp.zipจากเมือง Austin, TX
  2. เขตอำนาจศาล: เขตแดนจากเมือง Austin, TX

นี่คือขั้นตอนที่ฉันใช้ในการประมวลผลข้อมูล:

ArcGIS

  1. เพิ่มชุดข้อมูลไปยัง ArcMap
  2. ตั้งค่าระบบพิกัดเป็นฟุตเท็กซัสส่วนกลาง (srid 2277)
  3. ใช้เครื่องมือทางแยกจากเมนูแบบเลื่อนลง

PostGIS

นำเข้าพัสดุโดยใช้:

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 ที่จะเพิ่มการตรวจสอบจุดตัดของกล่องเพื่อเพิ่มความเร็ว ลองเพิ่ม 'และ a.geom && b.geom' ลงใน WHERE clause ของคุณและดูว่ามันสร้างความแตกต่างได้มากแค่ไหน
ฌอน

2
st_intersects () รวมถึงการสืบค้นกล่อง bounding ก่อนที่จะทำการทดสอบทางแยกใด ๆ ใน postgis 2.x ดังนั้นน่าเสียดายที่จะไม่ประหยัดเวลา
THX1138

1
คุณสามารถเรียกใช้แบบสอบถามโดยใช้อธิบายการวิเคราะห์และโพสต์ผลลัพธ์
นาธาน W

1
คุณควรทราบด้วยว่าคุณกำลังเรียกใช้ชุดข้อมูลที่แตกต่างกันใน postgis vs arcgis เนื่องจากคุณบอกว่าคุณ neex เพื่อทำให้พวกมันถูกต้องที่จะได้รับการยอมรับ vy postgis
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)

อาจจะเร็วกว่าโดยไม่มีสหภาพ


13
ขอบคุณที่ทำให้ฉันลงไปที่ 3.63 นาที! ฉันจะไม่เคยคิดว่าสหภาพจะเร็วขึ้น คำตอบนี้จะทำให้ฉันคิดใหม่เกี่ยวกับวิธีการค้นหาจากนี้
THX1138

2
นี่มันเจ๋งมาก ผมมีกรณีที่ทำงานที่แบบสอบถาม st_intersection ของฉันคือการใช้เวลา 30 นาที + และตอนนี้ฉันรู้ว่าฉันสามารถหลีกเลี่ยง :) ว่า
นาธาน W

1
คำถามนี้ทำให้ฉันเรียนรู้ Postgis! ฉันจะนอนหลับได้ดีในวันนี้เห็น Postgis วิ่งเคียงบ่าเคียงไหล่กับ Arcgis :-)
vinayan

2
อีกหนึ่งการปรับปรุงจาก Martin Davis คุณสามารถแทรก "เข้าหรือออก" คำถามใน SELECT โดยใช้คำสั่ง CASE และหลีกเลี่ยง UNION ด้วยวิธีดังกล่าว
พอลแรมซีย์

2
UNIONกำจัดแถวที่ซ้ำกันออกจากแบบสอบถามทั้งสอง แต่แบบสอบถามทั้งสองนี้ไม่สามารถมีแถวเดียวกันในชุดผลลัพธ์ ดังนั้นการUNION ALLข้ามการตรวจสอบซ้ำซึ่งจะเหมาะสมที่นี่ (ฉันไม่ค่อยได้ใช้UNIONมากนัก แต่โดยทั่วไปฉันพบว่าบ่อยครั้งที่ฉันทำฉันใช้บ่อยUNION ALLกว่า)
jpmc26

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"

ไม่เขาต้องการรูปหลายเหลี่ยมจุดตัดผลลัพธ์ แต่คำถามของคุณแสดงให้เห็นอย่างชัดเจนว่าความเจ็บปวดทั้งหมดอยู่ในการสร้างจุดตัดไม่ใช่ในส่วนทดสอบไบนารีจริง / เท็จ และคาดว่าจะค่อนข้างเนื่องจากรหัสจริง / เท็จนั้นได้รับการปรับปรุงอย่างมากในขณะที่การสร้างจุดตัดไม่ได้เป็นเช่นนั้น
พอลแรมซีย์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.