Приобретение 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!

Конечно, есть много причин, по которым 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, включая создание самой виртуальной машины .

Я также увеличил общую память с 24 МБ до 6 ГБ в файле 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. Посылки: tcad_parcels_06142012.shp.zip из города Остин, Техас
  2. Юрисдикция: Юрисдикционные границы из города Остин, штат Техас

Вот шаги, которые я предпринял для обработки данных:

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 минут на чистой геометрии!

THX1138
источник
2
В PostGIS широко распространена идиома, чтобы добавить проверку пересечения ограничивающей рамки, чтобы ускорить процесс. Попробуйте добавить 'AND a.geom && b.geom' к вашему предложению WHERE и посмотрите, насколько это существенно.
Шон
2
st_intersects () включает запрос ограничивающего прямоугольника перед выполнением любого теста пересечения в postgis 2.x, так что, к сожалению, это не сэкономит время.
THX1138
1
Можете ли вы выполнить свой запрос с помощью EXPLAIN ANALYZE и опубликовать результаты
Nathan W
1
вы также должны знать, что вы используете разные наборы данных на postgis vs arcgis, так как вы говорите, что вы должны сделать их действительными для принятия через postgis.
Никлас Авен
2
Можно ли заставить наборы данных взглянуть на это?
Никлас Авен

Ответы:

87

Другой подход. Зная, что боль в ST_Intersection, и что тесты true / false выполняются быстро, попытка минимизировать количество геометрии, проходящей через пересечение, может ускорить процесс. Например, участки, которые полностью содержатся в юрисдикции, не нужно обрезать, но 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! сегодня я буду спать спокойно, увидев, как Постгис бежит плечом к плечу с Арджисом :-)
vinayan
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"
Винаян
источник
Нет, ему нужны результирующие полигоны пересечений, но ваш запрос очень хорошо демонстрирует, что вся боль заключается в генерации пересечений, а не в бинарной части запроса «истина / ложь». И это вполне ожидаемо, поскольку код true / false сильно оптимизирован, а генерация пересечений - нет.
Пол Рэмси