Как быстро следует ожидать, что PostGIS геокодирует хорошо отформатированные адреса?
Я установил PostgreSQL 9.3.7 и PostGIS 2.1.7, загрузил данные о стране и все данные о состоянии, но обнаружил, что геокодирование намного медленнее, чем я ожидал. Я поставил свои ожидания слишком высоко? Я получаю в среднем 3 отдельных геокода в секунду. Мне нужно сделать около 5 миллионов, и я не хочу ждать три недели для этого.
Это виртуальная машина для обработки гигантских матриц R, и я установил эту базу данных сбоку, чтобы конфигурация выглядела немного глупо. Если серьезное изменение в конфигурации виртуальной машины поможет, я могу изменить конфигурацию.
Аппаратные характеристики
Память: 65 ГБ Процессоры: 6
lscpu
дает мне это:
# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 6
On-line CPU(s) list: 0-5
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 6
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 58
Stepping: 0
CPU MHz: 2400.000
BogoMIPS: 4800.00
Hypervisor vendor: VMware
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 30720K
NUMA node0 CPU(s): 0-5
ОС является Centos, uname -rv
дает это:
# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015
Конфиг Postgresql
> select version()
"PostgreSQL 9.3.7 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit"
> select PostGIS_Full_version()
POSTGIS="2.1.7 r13414" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.2, released 2012/10/08" LIBXML="2.7.6" LIBJSON="UNKNOWN" TOPOLOGY RASTER"
Основываясь на предыдущих предложениях к этим типам запросов, я увеличил shared_buffers
в postgresql.conf
файле до 1/4 доступной оперативной памяти и эффективный размер кэша до 1/2 оперативной памяти:
shared_buffers = 16096MB
effective_cache_size = 31765MB
У меня installed_missing_indexes()
и (после устранения дублирующих вставок в некоторые таблицы) ошибок не было.
Пример геокодирования SQL # 1 (пакетный) ~ среднее время составляет 2,8 / сек
Я следую примеру из http://postgis.net/docs/Geocode.html , который заставляет меня создать таблицу, содержащую адрес геокодирования, а затем выполнить SQL UPDATE
:
UPDATE addresses_to_geocode
SET (rating, longitude, latitude,geo)
= ( COALESCE((g.geom).rating,-1),
ST_X((g.geom).geomout)::numeric(8,5),
ST_Y((g.geom).geomout)::numeric(8,5),
geo )
FROM (SELECT "PatientId" as PatientId
FROM addresses_to_geocode
WHERE "rating" IS NULL ORDER BY PatientId LIMIT 1000) As a
LEFT JOIN (SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom
FROM addresses_to_geocode As ag
WHERE ag.rating IS NULL ORDER BY PatientId LIMIT 1000) As g ON a.PatientId = g.PatientId
WHERE a.PatientId = addresses_to_geocode."PatientId";
Я использую пакет размером 1000 и выше, и он возвращается через 337,70 секунд. Это немного медленнее для небольших партий.
Пример геокодирования SQL # 2 (строка за строкой) ~ среднее время составляет 1,2 / сек
Когда я копаюсь в своих адресах, выполняя геокодирование по одному за один раз с оператором, который выглядит следующим образом (кстати, пример ниже занял 4,14 секунды),
SELECT g.rating, ST_X(g.geomout) As lon, ST_Y(g.geomout) As lat,
(addy).address As stno, (addy).streetname As street,
(addy).streettypeabbrev As styp, (addy).location As city,
(addy).stateabbrev As st,(addy).zip
FROM geocode('6433 DROMOLAND Cir NW, MASSILLON, OH 44646',1) As g;
это немного медленнее (2,5 раза на запись), но я могу посмотреть на распределение времени запросов и увидеть, что это меньшинство длинных запросов, которые замедляют это больше всего (только первые 2600 из 5 миллионов имеют время поиска). То есть верхние 10% занимают в среднем около 100 мс, нижние 10% - в среднем 3,69 секунды, а среднее значение составляет 754 мс, а медиана - 340 мс.
# Just some interaction with the data in R
> range(lookupTimes[1:2600])
[1] 0.00 11.54
> median(lookupTimes[1:2600])
[1] 0.34
> mean(lookupTimes[1:2600])
[1] 0.7541808
> mean(sort(lookupTimes[1:2600])[1:260])
[1] 0.09984615
> mean(sort(lookupTimes[1:2600],decreasing=TRUE)[1:260])
[1] 3.691269
> hist(lookupTimes[1:2600]
Другие мысли
Если я не смогу добиться увеличения производительности на порядок, я подумал, что мог бы, по крайней мере, сделать обоснованное предположение о прогнозировании медленного времени геокодирования, но мне не очевидно, почему медленные адреса, кажется, занимают намного больше времени. Я запускаю исходный адрес через шаг пользовательской нормализации, чтобы убедиться, что он правильно отформатирован, прежде чем geocode()
функция получит его:
sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")
где myAddress
- [Address], [City], [ST] [Zip]
строка, скомпилированная из таблицы адресов пользователей из базы данных, отличной от postgresql.
Я попытался (не удалось) установить pagc_normalize_address
расширение, но не ясно, принесет ли это то улучшение, которое я ищу.
Отредактировано, чтобы добавить информацию мониторинга согласно предложению
Производительность
Один ЦП привязан: [редактировать, только один процессор на запрос, поэтому у меня 5 неиспользуемых ЦП]
top - 14:10:26 up 1 day, 3:11, 4 users, load average: 1.02, 1.01, 0.93
Tasks: 219 total, 2 running, 217 sleeping, 0 stopped, 0 zombie
Cpu(s): 15.4%us, 1.5%sy, 0.0%ni, 83.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 65056588k total, 64613476k used, 443112k free, 97096k buffers
Swap: 262139900k total, 77164k used, 262062736k free, 62745284k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3130 postgres 20 0 16.3g 8.8g 8.7g R 99.7 14.2 170:14.06 postmaster
11139 aolsson 20 0 15140 1316 932 R 0.3 0.0 0:07.78 top
11675 aolsson 20 0 135m 1836 1504 S 0.3 0.0 0:00.01 wget
1 root 20 0 19364 1064 884 S 0.0 0.0 0:01.84 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.06 kthreadd
Пример активности диска на разделе данных, в то время как один процесс привязан к 100%: [edit: только один процессор используется этим запросом]
# dstat -tdD dm-3 1
----system---- --dsk/dm-3-
date/time | read writ
12-06 14:06:36|1818k 3632k
12-06 14:06:37| 0 0
12-06 14:06:38| 0 0
12-06 14:06:39| 0 0
12-06 14:06:40| 0 40k
12-06 14:06:41| 0 0
12-06 14:06:42| 0 0
12-06 14:06:43| 0 8192B
12-06 14:06:44| 0 8192B
12-06 14:06:45| 120k 60k
12-06 14:06:46| 0 0
12-06 14:06:47| 0 0
12-06 14:06:48| 0 0
12-06 14:06:49| 0 0
12-06 14:06:50| 0 28k
12-06 14:06:51| 0 96k
12-06 14:06:52| 0 0
12-06 14:06:53| 0 0
12-06 14:06:54| 0 0 ^C
Проанализируйте этот SQL
Это из EXPLAIN ANALYZE
этого запроса:
"Update on addresses_to_geocode (cost=1.30..8390.04 rows=1000 width=272) (actual time=363608.219..363608.219 rows=0 loops=1)"
" -> Merge Left Join (cost=1.30..8390.04 rows=1000 width=272) (actual time=110.934..324648.385 rows=1000 loops=1)"
" Merge Cond: (a.patientid = g.patientid)"
" -> Nested Loop (cost=0.86..8336.82 rows=1000 width=184) (actual time=10.676..34.241 rows=1000 loops=1)"
" -> Subquery Scan on a (cost=0.43..54.32 rows=1000 width=32) (actual time=10.664..18.779 rows=1000 loops=1)"
" -> Limit (cost=0.43..44.32 rows=1000 width=4) (actual time=10.658..17.478 rows=1000 loops=1)"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode addresses_to_geocode_1 (cost=0.43..195279.22 rows=4449758 width=4) (actual time=10.657..17.021 rows=1000 loops=1)"
" Filter: (rating IS NULL)"
" Rows Removed by Filter: 24110"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode (cost=0.43..8.27 rows=1 width=152) (actual time=0.010..0.013 rows=1 loops=1000)"
" Index Cond: ("PatientId" = a.patientid)"
" -> Materialize (cost=0.43..18.22 rows=1000 width=96) (actual time=100.233..324594.558 rows=943 loops=1)"
" -> Subquery Scan on g (cost=0.43..15.72 rows=1000 width=96) (actual time=100.230..324593.435 rows=943 loops=1)"
" -> Limit (cost=0.43..5.72 rows=1000 width=42) (actual time=100.225..324591.603 rows=943 loops=1)"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode ag (cost=0.43..23534259.93 rows=4449758000 width=42) (actual time=100.225..324591.146 rows=943 loops=1)"
" Filter: (rating IS NULL)"
" Rows Removed by Filter: 24110"
"Total runtime: 363608.316 ms"
Посмотреть более подробную разбивку можно по адресу http://explain.depesz.com/s/vogS
Ответы:
Я провел много времени, экспериментируя с этим, я думаю, что лучше публиковать отдельно, так как они под другим углом.
Это действительно сложная тема, подробности см. В моем блоге о настройке сервера геокодирования и сценарии, который я использовал . Вот лишь несколько кратких резюме:
Сервер с данными только 2 состояний всегда быстрее, чем сервер, загруженный всеми данными 50 состояний.
Я проверял это на своем домашнем компьютере в разное время и на двух разных серверах Amazon AWS.
Мой бесплатный сервер уровня AWS с данными о 2 состояниях имеет только 1 ГБ ОЗУ, но обеспечивает стабильную производительность от 43 до 59 мс для данных с 1000 и 45 000 записей.
Я использовал точно такую же процедуру установки для сервера AWS 8G RAM со всеми загруженными состояниями, точно такими же сценарием и данными, и производительность упала до 80 ~ 105 мс.
Моя теория заключается в том, что, когда геокодер не может точно соответствовать адресу, он начинает расширять диапазон поиска и игнорирует некоторую часть, например почтовый индекс или город. Вот почему документ геокодирования может похвастаться тем, что он может повторно определить адрес с неверным почтовым индексом, хотя это заняло 3000 мс.
Если загружены только данные о 2 состояниях, серверу потребуется гораздо меньше времени для бесполезного поиска или поиска с очень низкой оценкой, поскольку он может выполнять поиск только в 2 состояниях.
Я попытался ограничить это, установив
restrict_region
параметр для мультиполигонов состояний в функции геокодирования, надеясь, что это позволит избежать бесполезного поиска, так как я почти уверен, что большинство адресов имеют правильное состояние. Сравните эти две версии:Единственное отличие второй версии заключается в том, что обычно, если я сразу же запускаю тот же запрос, он будет выполняться намного быстрее, потому что связанные данные были кэшированы, но вторая версия отключила этот эффект.
Так что
restrict_region
не работает, как я хотел, может быть, он просто использовался для фильтрации результатов многократного попадания, а не для ограничения диапазонов поиска.Вы можете немного настроить свой postgre conf.
Обычно подозреваемый в установке отсутствующих индексов, анализ вакуума не имел для меня никакого значения, потому что скрипт загрузки уже выполнил необходимое обслуживание, если только вы не испортили его.
Однако установка postgre conf в соответствии с этим постом помогла. Мой полноразмерный сервер с 50 состояниями имел 320 мс с конфигурацией по умолчанию для некоторых данных худшей формы, он улучшился до 185 мс с 2G shared_buffer, 5G кешем и пошел до 100 мс далее, с большинством настроек, настроенных в соответствии с этим постом.
Это больше относится к postgis, и их настройки кажутся похожими.
Размер партии каждого коммита не имел большого значения для моего случая. В документации по геокодированию использовалась партия размером 3. Я экспериментировал со значениями от 1, 3, 5 до 10. Я не нашел существенной разницы с этим. С меньшим размером партии вы делаете больше коммитов и обновлений, но я думаю, что настоящая бутылочная горловина не здесь. На самом деле я использую размер партии 1 сейчас. Поскольку всегда есть какой-то неожиданный неверно сформированный адрес, это вызовет исключение, я установлю весь пакет с ошибкой как проигнорированный и продолжу для оставшихся строк. При размере пакета 1 мне не нужно обрабатывать таблицу во второй раз, чтобы геокодировать возможные хорошие записи в пакете, помеченные как проигнорированные.
Конечно, это зависит от того, как работает ваш пакетный скрипт. Я опубликую свой сценарий с более подробной информацией позже.
Вы можете попытаться использовать нормализованный адрес, чтобы отфильтровать неверный адрес, если он подходит вам. Я видел, как кто-то где-то упоминал об этом, но я не был уверен, как это работает, поскольку функция нормализации работает только в формате, он не может точно сказать, какой адрес является недействительным.
Позже я понял, что если адрес явно не в форме и вы хотите их пропустить, это может помочь. Например, у меня много адресов, в которых отсутствуют названия улиц или даже названия улиц. Сначала нормализовать все адреса будет относительно быстро, затем вы можете отфильтровать очевидный неверный адрес, а затем пропустить их. Однако это не подходило для моего использования, поскольку адрес без номера улицы или даже названия улицы все еще можно сопоставить с улицей или городом, и эта информация все еще полезна для меня.
И большинство адресов, которые не могут быть геокодированы в моем случае, на самом деле имеют все поля, просто нет совпадений в базе данных. Вы не можете фильтровать эти адреса, просто нормализуя их.
РЕДАКТИРОВАТЬ Для получения более подробной информации см. Мой пост в блоге о настройке сервера геокодирования и сценарии, который я использовал .
РЕДАКТИРОВАТЬ 2 Я закончил геокодирование 2 миллиона адресов и сделал много очистки на основе результатов геокодирования. Благодаря более чистому вводу следующее пакетное задание выполняется намного быстрее. Под чистым я подразумеваю, что некоторые адреса явно неверны и должны быть удалены, или имеют геокодер с неожиданным содержимым, чтобы вызвать проблемы с геокодированием. Моя теория такова: удаление плохих адресов может помочь избежать путаницы в кеше, что значительно повышает производительность хороших адресов.
Я разделил входные данные на основе состояния, чтобы убедиться, что в каждом задании все данные, необходимые для геокодирования, кэшируются в оперативной памяти. Однако каждый неверный адрес в задании заставляет геокодер искать в большем количестве состояний, что может испортить кеш.
источник
Согласно этой ветке обсуждения , вы должны использовать одну и ту же процедуру нормализации для обработки данных Tiger и вашего входного адреса. Поскольку данные Tiger были обработаны с помощью встроенного нормализатора, лучше использовать только встроенный нормализатор. Даже если вы запустили pagc_normalizer, он может вам не помочь, если вы не используете его для обновления данных Tiger.
При этом я думаю, что geocode () все равно вызовет нормализатор, поэтому нормализация адреса до геокодирования может быть не очень полезна. Одним из возможных применений нормализатора может быть сравнение нормализованного адреса и адреса, возвращаемого функцией geocode (). С их нормализацией было бы легче найти неправильный результат геокодирования.
Если вы можете отфильтровать неверный адрес из геокода с помощью нормализатора, это действительно поможет. Однако я не вижу, чтобы у нормализатора было что-то вроде оценки матча или рейтинга.
В той же ветке обсуждения также упоминался переключатель отладки,
geocode_address
чтобы показать больше информации. Узлуgeocode_address
нужен нормализованный адрес ввода.Геокодер быстр для точного соответствия, но в трудных случаях отнимает гораздо больше времени. Я обнаружил, что есть параметр,
restrict_region
и подумал, что, возможно, он будет ограничивать бесполезный поиск, если я установлю ограничение как состояние, поскольку я почти уверен, в каком состоянии он будет находиться. Оказалось, что установка его в неправильное состояние не помешала геокоду получить правильный адрес, хотя это займет некоторое время.Так что, возможно, геокодер будет искать во всех возможных местах, если первый точный поиск не соответствует. Это позволяет обрабатывать ввод с некоторыми ошибками, но также делает поиск очень медленным.
Я думаю, что интерактивному сервису полезно принимать ввод с ошибками, но иногда мы можем просто отказаться от небольшого набора неправильных адресов, чтобы повысить производительность при пакетном геокодировании.
источник
restrict_region
на время, когда вы устанавливаете правильное состояние? Кроме того, из ветки постгис-пользователей, на которую вы ссылались выше, они упоминают, в частности, проблемы с адресами, с1020 Highway 20
которыми я столкнулся.Я собираюсь опубликовать этот ответ, но, надеюсь, другой участник поможет разобраться в следующем, которое, я думаю, нарисует более целостную картину:
Теперь мой ответ, который является просто анекдотом:
Лучшее, что я получаю (на основе одного соединения), составляет в среднем 208 мс за
geocode
. Это измеряется путем случайного выбора адресов из моего набора данных, который распространяется по всей территории США. Он включает в себя некоторые грязные данные, но самые длительные операцииgeocode
не кажутся плохими очевидными способами.Суть в том, что я, кажется, привязан к процессору, и что один запрос привязан к одному процессору. Я могу распараллелить это, запустив несколько соединений с
UPDATE
дополняющими сегментамиaddresses_to_geocode
таблицы в теории. Тем временем я получаюgeocode
в среднем 208 мс для общенационального набора данных. Распределение искажено как с точки зрения того, где находится большинство моих адресов, так и с точки зрения того, сколько времени они занимают (например, см. Гистограмму выше) и таблицы ниже.Мой лучший подход до сих пор - делать это партиями по 10000 с некоторым ощутимым улучшением по сравнению с увеличением количества партий. Для партий из 100 я получал около 251 мс, из 10000 я получал 208 мс.
Я должен цитировать имена полей из-за того, как RPostgreSQL создает таблицы с
dbWriteTable
Это примерно в 4 раза быстрее, чем если бы я делал их по одной записи за раз. Когда я делаю их по одному, я могу получить разбивку по штатам (см. Ниже). Я сделал это, чтобы проверить и убедиться, что одно или несколько состояний TIGER имеют плохую нагрузку или индекс, что, как я ожидал, приведет к
geocode
снижению производительности в зависимости от состояния. Я, очевидно, получил некоторые неверные данные (некоторые адреса даже адреса электронной почты!), Но большинство из них хорошо отформатированы. Как я уже говорил, некоторые из самых длительных запросов не имеют явных недостатков в своем формате. Ниже приведена таблица с числом, минимальным временем запроса, средним временем запроса и максимальным временем запроса для состояний из 3000 (несколько случайных адресов из моего набора данных):источник