Набуття 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 може використовувати лише 4 ГБ оперативної пам’яті, тоді як я міг використовувати до 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 .

Я також збільшив спільну пам'ять з 24 Мб за замовчуванням до 6 ГБ у файлі conf та запустив наступні команди, щоб дозволити запускати поштові записи:

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. Встановіть систему координат на центральні ноги техаса (srid 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, щоб додати перевірку перетину обмежувальної коробки, щоб прискорити роботу. Спробуйте додати "AND a.geom && b.geom" до пункту WHERE і подивіться, наскільки це має значення.
Шон

2
st_intersects () включає запит на обмежувальний ящик перед тим, як проводити тестування перехрестя в postgis 2.x, так що, на жаль, це не економить жодного часу.
THX1138

1
Чи можете ви запустити запит за допомогою EXPLAIN ANALYZE та опублікувати результати
Nathan W

1
Ви також повинні знати, що ви використовуєте різні набори даних про postgis vs arcgis, оскільки ви говорите, що ви неexe, щоб зробити їх дійсними, щоб вони були прийняті у 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 хвилин + і тепер я знаю, як я можу цього уникнути :)
Nathan W

1
це питання змусило мене навчитися Postgis! сьогодні я буду спати добре, бачачи Постгіса, що біжить плечем до плеча з Аркгісом :-)
вiн

2
Ще одним вдосконаленням від Мартіна Девіса, ви могли б накреслити "увійти чи вийти"? поставити під сумнів 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"

Ні, він хоче отримати полігони перетину, але ваш запит дуже добре демонструє, що вся біль припадає на генерацію перехрестя, а не в бінарній істинній / хибній тестовій частині запиту. І це цілком очікувано, оскільки справжній / хибний код сильно оптимізований, а генерація перехрестя - ні.
Пол Рамзі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.